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 Šućur

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 ežan a B isenić
T ehnički 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 ataša Pavlov
P relom tek sta i o b r a d a slika San ja Tasić
M iiica D ečan sk i
N ataša 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 išite 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 beležen je, b ez p r e th o d n e p ism e n e d o z v o le izdavača.

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 sadržaj
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 izvršavanja 99
5: Inicijalizacija i čišćenje 115
6: K ontrola p ristupa 159
7: Ponovno korišćenje klasa 180
8: Polim orfizam 211
9: Interfejsi 239
10: U nutrašnje klase 266
11: Čuvanje objekata 302
12: O b rad a grešaka pom oću izuzetaka 345
13: Znakovni nizovi 392
14: Podaci o tip u 432
15: G enerički 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 izvršavanje 885
22: Grafička korisnička okruženja 1045
A: D odaci 1166
B: Resursi 1170
Spisak term in a korišćenih u knjizi 1175
Indeks 1177
Sadržaj
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
Učenje Ja v e .......................................9 G de se nalazi skladište............................43
C ilje v i................................................9 Specijalan slučaj: p rosti tip o v i............44
Poučavanje 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 važenja......................................46
V ežbe................................................11
Oblast važenja 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
Način pisanja korisćen 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
Koriščenje d rugih k o m p o n e n a ta . . . . 52
O bjekat pruža 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 korišćenje realizacije.. .21 Prevodenje i iz v ršav a n je ....................... 56
N asleđivanje...................................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 greškam 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
Korišćenje 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
Matematički 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 uvećanje I’o 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 poređenja......................73
iz k o n s tr u k to r a ..................................... 127
Ispitivanje jednakosti objekata.........73
Z naćenje rezervisane reči s t a t i c . . . . 129
Logički 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 služi 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 greške prilikom Redosled in ic ija liz a cije ....................... 139

korišćenja operatora......................86 Inicijaiizacija statićnih elem enata .. 140


I:ksplicitna inicijalizacija statićnih
Eksplicitna konverzija tipova . . .87
e le m e n a ta ................................................. 143
Odsecanje i zaokruživanje..............88 Inicijaliz.acija nestatićn 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 određivanje velićine................. 89 N a b r o ja n i t i p o v i ............................... 155
Sažet pregleđ o p e ra to ra ............... 89 S a ž .e ta k .................................................... 158
Sažetak............................................. 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
Logičke 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 Lićna bihlioteka sa a la tk a m a ............ 166
Petlja f o r ...................................................101 K orišćenje tivoženja za p ro m e n u
O p e ra to r z a r e z ....................................... 103 po n ašan 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 reči 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
Sadržaj v ii

Interfejs i realizacija....................174 Projektovanje pom oću


Pristup k la s a m a ..........................175 n asleđ iv an ja ................................ 2 3 3
Sažetak........................................... 178 P oredenje zam ene i proširivanja . . . 235
Svođenje naviše i podaci o tip u
p rilikom iz v rša 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
Sažetak........................................... 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 čišćenja . . . 191 Višestruko nasleđivanje u Javi. .251
Sakrivanje im e n a .................................. 194 Proširivanje interfejsa
I z b o r iz m e đ u k o m p o z ic ije nasleđivanjem .............................. 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
Prilagođavanje interfejsu...........256
Z ašto „svođenje naviše“ ? .....................199 Polja u in te rfe jsim a ....................258
Ponovo o hiran ju izm eđu Inicijalizacija polja u interfejsim a . . 259
kom pozicije i n a sle đ iv a n ja................ 200 Ugnežđivanje 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 Proizvođači............... 262
Finalni p o d a c i .......................................200 Sažetak........................................... 265
Finalne m e t o d e .....................................204
Finalne klase............................................206
Fažljivo sa f i n a l .....................................207
10: Unutrašnje klase 266
Inicijalizađja i učitavanje Pravljenje unutrašnjih 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
Sažetak........................................... 210 Unutrašnje klase i svođenje
naviše..............................................271
8: P o l i m o r f i z a m 211 Unutrašnje klase u m etodam a
Ponovo o svođenju naviše.........211 i oblastim a važenja......................273
Z an em ariv an je tipa o b jc k ta ..............213 A nonim ne unutrašnje klase . . .275
Z ačko ljica.....................................214 Ponovo o p ro izvodnim m e to d a m a . 279
Vezivanje poziva m e to d a .....................214 Ugneždene klase.......................... 2 8 2
D obijanje p ravilnog ponašanja . . . . 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 spoljašnjosti iz višestruko
( 'ireška: ,,redel l n isan je“ ugnežđenih k la s a .................................. 284
priv atn ih m e to d a .................................. 221 Zašto unutrašnje k lase?............. 285
Cireška: polja i statićne m e to d a ......... 222 Zaključci i p o v ra tn i p o z iv i................ 287
K onstruktori i p olim orfizam .. .224 U nu trašn 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 čišćenje......................... 226 Nasleđivanje unutrašnjih
Ponašanje 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 unutrašnja klasa može
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 redefiniše?.......................... 297
v iii Misliti na Javi

Lokalne unutrašnje 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 unutrašnjih klasa.300 Č em u služi o d red b a finally?..............368
Sažetak...........................................301 U potreba bloka finally tokom
p o v ratk a iz m e to d e p o m o ć u
rezervisane reči 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

Generičke 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 sadržaja kontejnera. .309
Perspektive.............................................. 386
L iste............................................... 311 Prosleđivanje 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
Ulančana 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 ulančane 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
Poređenje preklapanja
(od ulančane 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
Poređenje kolekcija i Iteratora .332 Operacije sa znakovnim
Foreach i iteratori........................336 n izov im a....................................... 398
Adapterska metoda.......................338 Formatiranje izlaza......................400
Sažetak...........................................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
Položaj nastanka izuzetka na steku
izvršavanja..........................................358
Leksičko analiziranje ulaza . . . .427
C ran ičnici k la se S c a n n e r .................. 42l>
Ponovno generisanje izu z e tk a ........ 359
l.eksička anali/a p om oću
Ulančavanje 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 slučaj klase
Klasa S tringT okenizer............... 431
R untim eE xcep tio n........................... 305 Sažetak........................................... 4 3 1
Sadržaj 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 izvršavanja............... 432 Pravljenje instanci tip o v a ...................523
Objekat tipa Class........................434 Nizovi generičkih tip o v a .....................526
Literali k la se ........................................... 439 G r a n i c e ...................................................... 531
G eneričke 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
Korišćenje literala ld ase....................... 451 N eograničeni džokerski
D inam ićki 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
Poređenje 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 izvršavanja....................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
Dinamički posređnici................. 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

Lažni objekti i vezivne funkcije . . . . 476 G enerički ko d koji se neobično


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
Sažetak........................................... 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: Generički tipovi 484 I z u z e c i .................................................... 563
Poređenje sa C + + - 0 1 1 1 ............... 485 M i k s i n i ................................................. 565
M iksini u jeziku C + + ......................... 566
Jednostavni generički tipovi . . .485
M ešanje p o m o ć u interfejsa................ 567
Bihlioteka n - t o r k i ................................ 487
K orišćenje obrasca D e c o r a to r ......... 568
Klasa s t e k a ..............................................490
M iksini s d inam ičkim
N a s u in ic n a L is ta .................................. 491
p o s re d n ic im a ..........................................570
Generički interfejsi..................... 492
L a t e n t n i t i p o v i .................................. 572
Generičke m etode........................496
K o m p e n z a c ija za n e p o s t o ja n je
Korišćenje zakljućivanja 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 generičke m e to d e ................................499 Priinena m etode na se k v e n c u ........ 577
G enerićka m etoda za u p o treh u Kada sltićajno neinate
s G e n e r a to r im a .....................................500 odgovarajuc'i interfejs....................... 580
G e n e ra to r opšte 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 služna 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 Pročitajte 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 razlićitih L is ta ..............688
Vraćanje 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
Višedimenzionalni n iz o v i.........599
Iz b o rsk u p a
Nizovi i generički 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 retraživanje 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 ućiniti
Kopiranje niza..............................617 n e p ro m e n ljiv o m .................................. 706
Poređenje nizova.......................... 618 Sinhronizovanje kolekcije ili m a p e . .708
Poređenje elemenata niza..............619 Čuvanje referen ci ............................... 7 0 9
Uređivanje niza............................ 623 K ontejner W e a k H a s h M a p ................ 712
Pretraživanje uređenog niza.......... 624 Kontejneri Jave 1 . 0 / 1 . 1 ....................7 1 3
Sažetak........................................... 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
Sažetak .........................................................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 rešen je............................630
G en erato ri m a p a .................................. 632 Klasa File ................................................... 7 1 9
K orišćenje 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 služne m eto d e za d ire k to riju m e . . 723
l’rovera 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 skladištenja. .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 ponašanja 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 aćin 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 kljućeva 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
Sadržaj 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 nasleđuje............... 813
Č uvanje i rek on stru isan je p o d atak a. .742
Nasumičan iz b o r ........................814
C itan je i upisivanje d ato tek a
s n a su m ičn 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 Korišćenje 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 ođgovornosti
u P r in tW rite r..........................................750 pomoću nabrojanih tipova............ 826
P reu sm erav an je stan d a rd n o g Mašine 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 Višekratno otkrivanje tip a .........836
N o v e U / I k l a s e ..................................... 7 5 3 Otkrivanje tipa pomoću
Konverzija p o d a ta k a ............................757 nabrojanih tipova........................ 838
Pribavljanje p ro stih tip o v a ................ 759 Korišćenje 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 pomoću mapa
D atoteke preslikane u m e m o riju . . . 769 EnumMap...................................842
Z akljućavanje d a to te k a ....................... 772 Korišćenje 2-D niza.......................843
K o m p r i m o v a n j e .................................. 7 7 5 Sažetak........................................... 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 većeg 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
Pronalaženje 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
Korišćenje tr a jn o s ti .............................. 793 Ogra n ičenj 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 podržavaju
nasleđivanje........................................ 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 mogućnosti Upotreba obrasca Visitor
nabrojanih tip o v a........................805 sa alatkom a p t.............................. 861
Uvoz slalićnih članovii Jedinično testiranje pom oću
u nabrojani tip............................ . H06 a n o ta c ija .......................................864
Dodavanje metoda Testiranje generičkih 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
Sažetak........................................... 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
izvršavanja.....................................886 sa o d b ro ja v a n je m ................................ 983
Brže iz v ršav a n je .....................................886 C y c lic B a rrie r..........................................986
Poboljšan 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 izvršilaca 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 službenika . . 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 Poređenje tehnologija u zajam no
Servisne n i t i ............................................901 isključivih 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 ističk o z a k lju č a v a n je ............1034
Kako se p rid ru žiti postojećoj n i t i . . . 912 R ea d W rite L o c k .................................. 1036
Korisničko o k ruženje 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 usavršavanje . . . 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: Grafička korisnička
R azrešavanje takm ičenja okruženja 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 ritični 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 skladište n i t i ......................... 940
Višeredna polja za te k s t...........1054
G a š e n j e z a d a t a k a ............................... 9 4 2
Raspoređivanje 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 ašenje 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 događaja
n o tify () u o d n o su na n o tify A ll(). . . 962
Proizvođači i p o tr o š a č i....................... 965
grafičke biblioteke Svving.........1059
T ipovi dogadaja i p rije m n ik a ......... 1060
Proizvodači, p otrošači i redovi
Praćenje 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
Sadržaj 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 rafičke liste..........................................1081 Paralelno izvršavanje 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
Iskačući 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
Klizači 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 izvršavanje 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 višenitno 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
Složenija podrška za z r n a ................ 1137 S oftver......................................... 1170
V'iše o z r n im a ....................................... 1137 Programi za uređivanje
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 korišćenih
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 počctku satn prišao Javi kao „jošjednom 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 drugačija od nam ene svih drugih jezika koje sam sretao.
Program iranje je snalaženje u složenosti: složenost problem a koji želite da rešite dodaje
se na složenost računara na kome se problem rešava. Zbog ove složenosti većina naših pro-
gramskih projekata propadne.- Pa ipak, za gotovo nijedan program ski jezik za koji znam
nije odlučeno da bi njegov glavni cilj trebalo da bude savladavanje složenosti razvoja i ođr-
žavanja program a.1Naravno, mnoge odluke pri stvaranju program skih jezika donete su sa
složenošću na umu, ali u nekom trenutku uvek bi se našle neke druge stvari za koje se sma-
tralo da su neophodne u mešavini. Te druge stvari neizbežno 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 omogućio lak prelazak C program erim a), a pri
tom i efikasan. I jedno i drugo su veoma korisni ciljevi i um nogom e zaslužni za uspeh je-
zika C++, ali takođe unose dodatnu složenost zbog koje neki projekti ne bivaju završeni
(naravno, možete kriviti program ere i upravu, ali, ako jezik može da pom ogne tako što će
otkrivati vaše greške, zašto to ne bi i radio). Evo još jednog prim era. Visual Basic (VB) je
bio vezan za BASIC, koji baš nije bio proširiv jezik, tako da su sva proširenja nagom ilana u
VB-u proizvela zaista zastrašujuću sintaksu koja se veoma teško održava. 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 optuživan kako proizvodi ,,kod sam o za pisanje" (tj.
posle nekoliko meseci ne možete ga više čitati). S druge strane, pri stvaranju jezika kao što
su C++, VB, Perl i Smalltalk, deo napora je usmeren na rešavanje problem a složenosti, pa
oni veoma uspešno izlaze na kraj sa određenom vrstom problema.
Dok sam ućio Javu, čini mi se da je na m ene najviše uticalo to što je Sun m eđu svojim
ciljevima pri projektovanju imao i princip smanjenja složenosti zaprograrnera. Kao da su
rekli: „Važno nam je da ubrzam o i pojednostavim o pravljenje robusnog koda“. U ranim
danim a, ovaj princip je rezultovao kodom koji se nije brzo izvršavao (iako se to s vreme-
nom popravilo), ali je zaista zadivljujuće skratio vreme potrebno za pisanje program a
- za izradu program a na lavi potrebno je upola m anje vrem ena nego za pravijenje odgo-
varajućeg C ++ programa. Ovaj rezultat sam po sebi može da uštedi m nogo vrem ena i
novca, ali lava se tu ne zaustavlja. O na nastavlja da pakuje sve važne i složene zadatke, kao
što su višenitni rad i mrežno program iranje, u svojstva jezika ili bibliotcke koji pojedno-
stavljuju te zadatke. Najzad, latila se nekih zaista veoma složenih problem a kakvi su me-
duplattonnsko programiranje, dinamička prom ena koda, čak i pitanja zaštite —od kojih
se svaki na vašoj lestviei složenosti može svrstati od „prepreka'1do „potpuno nemoguće".
Stoga, uprkos problem ima s perform ansam a koje smo susreli, Java silno obećava: može
od nas da napravi znatno produktivnije programere.

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


2 Misliti na Javi

Java na sve načine povećava opseg komunikacije tneđu Ijudima: stvaranjem program a,
tim skim radom na stvaranju program a, izgradnjom korisničkog okruženja kao veze iz-
m eđu program a i l<orisnika, izvršavanjem program a na različitim tipovim a računara i
lakšim pisanjem program a kojima se pristupa preko Interneta.
Mislim da ogrom na količina inform acija koje putuju Internetom nije najvažniji po-
kazatelj značaja kom unikacione revolucije; prava revolucija je u tom e što ćemo moći
m nogo lakše da razgovaramo - pojedinačno, u grupam a, i sa celom planetom . Priča se da
bi naredna revolucija iznedrila svojevrstan globalni um proizašao iz iskustva više ljudi
i njihove m edusobne povezanosti. Java može ali i ne m ora, biti alat koji će podstaći tu
revoluciju - i zbog same takve m ogućnosti, osećam kako je značajno da i drugi nauče ovaj
jezik.

Java SE5 i SE6


Ovo izdanje knjige m nogo duguje poboljšanjim 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 poboljšaju iskustvo program era.
Kao što ćete videti, projektanti Jave nisu u tom e potpuno uspeli, ali su, uopšte uzev, na-
pravili veliki korak u pravom smeru.
Jedan od važnih ciljeva ovog izdanja bio je potpuno obuhvatanje poboljšanja Jave SE5/6
- da se ona objašnjavaju i upotrebljavaju u celoj knjizi. To znači da je ovo izdanje, donekle
hrabro, „samo za Javu SE5/6“ i da se d obar deo koda u knjizi neće prevesti pom oću ranijih
verzija Jave; ukoliko pokušate, sistem će se pobuniti i zaustaviti. Mislim ipak da su pred-
nosti ovog pristupa vredne tog rizika.
Ako ste prim orani da se služite ranijim verzijama Jave, na www.MindView.net možete
besplatno preuzeti prethodna izdanja ove knjige. Zbog mnogih razloga, odlučio sam da u
besplatnom elektronskom obliku ne ponudim tekuće izdanje knjige, nego samo prethodna.

Java SE6
Ova knjiga je bila ogrom an projekat koji je zahtevao mnogo vremena, i pre nego što je
izašla, pojavila se beta verzija Jave SE6 (pod radnim im enom mustartg). lako je lava SF.6
donela nekoiiko manjih izmena koje su poboljšale neke prim ere u knjizi, uglavnom nije
uticala na sadržaj ove kniige; glavna obeležja Jave SE6 su povećanje brzine izvršavanja i
novi elementi biblioteka, koja nisu tem a ove knjige.
Program i navedeni u knjiz.i uspešno su testirani na kandiđatskoj verziji Jave SF.6, pa ne
oćekujem da će ona doneti izmene koje bi uticale na sadržaj ove knjige. Ukoliko u vreme
objavljivanja zvanične verzije Jave SE6 bude važnih iz.mena, njih ćemo uvažiti u izvornom
kodu knjige koji se može preuzeti na adresi www.MiiidVicw.nct.
Na koricama knjige piše da je ona ,,za Javu SE5/6", što znači „napisana za |avu SF.5 i
veoma važne 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 pogrešno ili prosto zamorno. fednako tako, pisanje novog izdanja dovodi do fa-
scinantnih novih zamisli, a stid zbog pogrešaka m nogo je manji od uživanja u otkriču i
m ogućnosti da se zamisli izraze bolje nego pre.
Tu je i izazov da knjigu napišem tako da i vlasnici prethodnih izdanja požele da je ku-
pe. To me tera da poboijšavam ,prerađujem i premeštam sve što mogu da bi knjiga postala
novo i vredno iskustvo za posvećene č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 može preuzeti u obliku Flash
prezentacije. Polaznici tog seminara koji nedovoljno poznaju sintaksu C-a obučavaju se
da ovladaju m aterijalom iz ove knjige. Iako dva poglavlja ove knjige daju pristojan uvod
u sintaksu, ona m ožda neće biti dovoljna osobam a bez adekvatnog predznanja, a prezen-
tacija Thinking in C upravo njima pomaže da dostignu potreban nivo.
Poglavlje Paralelno izvršavanje(ranije„Višenitno izvršavanje11) potpuno je prerađeno da
bi odraziio velike izmene u odgovarajućim bibliotekama Jave SE5, ali još uvek pruža temelj
za osnovne koncepte paralelnog izvršavanja. Bez tog jezgra, teško ćete razumeti sioženije
oblasti paralelnog izvršavanja. 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.
Znaćajnim novim m ogućnostim a Jave SE5 posvećena su nova poglavlja, a ostale su
utkane u izmene postojećeg materijala. Pošto ja neprestano proučavam projektne obra-
sce, povećan je i njihov broj u knjizi.
U knjizi je biio m nogo premeštanja, u dobroj meri nadahnutih predavačkom 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 nepromišljenom verovanju da tema m ora biti „dovoljno
velika" kako bi zaslužila sopstveno poglavlje. Ali, naročito tokom predavanja projektnih
obrazaca, uvideo sam da polaznici seminara najbolje shvataju kada uvodim jedan po je-
dan obrazac i odm ah uradim odgovarajuću vežbu, čak i ako zbog toga tek nakratko go-
vorim . (Otkrio sam da je taj tempo prijatniji i za m ene kao predavača.) Zato sam u ovoj
verziji knjige pokušao da pođelim poglavlja na teme i da ne brinem koliko su duga. Čini
m i se da je tako bolje.
Uvideo sani i kolika je važnost testiranja programa. Ukoliko nem ate ugrađen sistem za
testiranje, u kojem se testiranjeobavlja svaki put kada generišete program, ne možete 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; naći ćete ga u kodu ove knjige koji se može preuzeti na lokaciji www.MindView.net.)
Testiranje uopšte razm otreno je u dodatku objavljenom na adresi http://M indView.net/
Books/Bctterjavn; tu su objašnjene osnovne veštine kojima bi, po m om mišljenju, svi pro-
gram eri trebalo da vladaju.
4 Misliti na Javi

Pored toga, pročešljao sam sve prim ere u knjizi i zapitao se: „Zašto sam to uradio baš
ovako?" U većini slučajeva nešto sam izm enio i poboljšao, da bi prim eri bili dosledniji i da
bih pokazao ono što sm atram najboljom praksom u pisanju Java koda (koliko se to može
u tekstu uvodnog nivoa). Dizajn i realizacija m nogih postojećih prim era, značajno 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, periodično se ja-
vljala ista prim edba „knjiga je preobimna". Po m eni, ako čitaocu smeta samo obim dela,
to i nije tako strašno (prisetim o se prim edbe austrijskog cara o M ocartovom delu: „Pre-
više nota!“, iako se ja ni na koji način ne poredim s M ocartom ). M ogu sam o da pretpo-
stavim kako takvu prim edbu upućuje onaj ko će se tek upoznati sa širinom Jave i ko nije
video ostale knjige o ovom jeziku. Ipak, pokušao sam da u ovom izdanju skratim delove
koji su postali nepotrebni ili barem ne previše značajni. Po praviiu, pokušao sam da uklo-
nim sve što više nije potrebno, ubacim izmene i poboljšam 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 veličinu 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 erička varijanta pokreta um etnost i zanatstvo
(American Art & Crafts Movement) koji se javio poćetkom 20. veka i dostigao zenit iz-
među 1900. i 1920. Nastao je u Engleskoj kao reakcija na mašinsku proizvodnju, indu-
strijsku revoluđiu i veoma izražen dekorativni stil viktorijanskog doba. Pokret se zalagao
za um eren dizajn i prirodne form e koie je prom ovisao um etnićki pravac art nuvo (fr. l'art
nouveau). Isticana je važnost um etnika kao pojedinca, a nije odbacivano korišćenje mo-
dernih alata. Postoje velike slićnosti s današnjom situacijom: prelazak u novi vek, napre-
dak od korena računarske revolucije do nečega plemenitijeg i značajnijeg za pojedinca,
isticanje veštine pisanja soltvera um esto ćiste proizvodnje koda.
Javu vidim kao pokušaj uzdizanja program era iznad nivoa tehničara 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 okruženi nam eštajem, lam pam a i drugim predm etim a koji ili potiću iz
tog perioda, ili su njime inspirisani.
T3rugi detalj na ovim koricam a je kutija za prikupljanje prirodnjaćkih eksponata - u
ovom slučaju 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 nameće 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 greške. 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 uhvaćene i, po svoj prilici, ubijene u tegli za uzorke i smeštene u male izložbene ku-
tije: sve ovo nagoveštava Javinu sposobnost da pronalazi, prikazuje i obuzdava bubice
(što je zbilja jedan od njenih najjačih 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 vaše strpljenje, dok i dalje pokušavam da napravim najbolji
model za m eđusobnu saradnju nezavisnih ljudi kao što smo mi.
O dnedavno, bez sum nje zaslugom interneta, povezao sam se sa iznenadujuće velikim
brojem ljudi koji m i pom ažu u poslu, uglavnom radeći u svojim kućnim kancelarijama.
U prošlo vreme, m orao bih da iznajmim prilično veliki kancelarijski prostor gde bih sme-
stio sve te ljude, ali zahvaljujući Mreži, ekspres pošti i telefonu, koristim njihovu pomoć
bez tih dodatnih troškova. Tokom m ojih pokušaja da naučim 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 tuđu
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 nešto ura-
dim ). Jonathan Wilcox, Esq. pročešljao je struk turu mog preduzeća, prevrnuo svaki ka-
men ispod kojeg se m ožda kriju škorpioni i naterao nas da prođem o kroz postupak
pravno valjanogorganizovanja. Hvala ti na staranju i upornosti. Sharlynn Cobaugh je na-
pravila od sebe stručnjaka za obradu zvuka i postala je bitan deo tim a za pravljenje mul-
timedijskih kurseva, kao i za rešavanje drugih problema. Hvala na upornosti koju
pokazuješ suočena s nepredvidivim računarskim problem im a. Ljudi iz praške 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 grafićkih rešenja.
Tokom mnogih godina, Gerald VVeinberg je putem svojih konferencija i radioriica
postao moj nezvanični trener i m entor, na čemu mu zahvaljujem.
Ervin Varga mi je izuzetno m nogo pom ogao svojim tehničkim ispravkama 4. izdanja
- iako su drugi pomagali u pojedinim poglavljima i prim erim a, Ervin je bio osnovni
tehnički recenzent cele knjige, a preradio je i vodič s rešenjima za 4. izdanje. Ervin je
pronašao greške i uneo poboljšanja u knjigu koja su nemerljiv doprinos ovom tekstu.
Njegova temeljnost i pažnja koju obraća na pojedinosti začuđujuće 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 oći kada sam
tražio tuđa mišljenja. 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 generićkim tipovima.
Hvala Marku VVelshu na njegovoj stalnoj pomoći.
Evan Cofskv mi stalno pruža veliku podršku svojim poznavanjem (napamet!) svih
zam ršenih pojedinosti o podešavanju i održavanju Web servera na Linuxu. On se stara da
MindVievvov server bude podešen i bezbedan.
6 Misliti na Javi

Posebno hvala m om novom prijatelju, kafi, koja mi je ulila gotovo neogranićen entu-
zijazam za ovaj projekat. Kafić Camp4 u gradu Crested Butte u Koloradu bio je stalno
sastajalište ljudi koji su dolazili na MindVievvove seminare, a tokom sem inarskih odm ora
pružao nam je najbolju opskrbu hranom i pićem 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 kafića koji tako veselo toče piće.
Hvala ljudima u Prentice Hallu na tolerantnosti i ogrom nom strpljenju kojim mi
obezbeđuju sve što tražim.
Određene 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) rešio mi je bezbroj problem a koje W indows neće/ne može, svakog
dana sam m u sve više privržen (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 očekujem da će i ubuduće davati velike rezultate,
pošto se i dalje razvija. (Od kad je to IBM postao kul? M ora da mi je nešto promaklo.) Jet-
Brains IntelliJ Idea nastavlja da krči nove kreativne puteve m eđu razvojnim alatkama.
Na ovoj knjizi počeo sam da koristim Sparxsystemsov Enterprise Architect i on je brzo
postao m oja omiljena UML alatka. U raznim prilikam a dobro je poslužio form ater koda
Jalopy Marca Hunsickera (www.triemax.com), a Marco je bio veoma susretljiv i konfigu-
risao ga prem a mojim specifičnim 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
početnike na seminarima.
1 naravno, ako to nisam već dovoljno puta rekao na svim mogućim mestim a, za reša-
vanje problem astalnokoristim Pvthon ( www.Pytlion.org). On je delo m o g drugara Guida
Van Rossuma i grupe uvrnutih genija s kojima sam trćao nekoliko divnih dana (Tim e Pe-
tersu, uram io sam miša kojeg si pozajmio i zvanićno ga nazvao „TimBotM iš"). Vi, m om -
ci, treba da nadete neko zdravije mesto za ručak. (Takođe, hvala celoj Pvthon zajednici,
oni su fenom enalna gomila ljudi.)
Mnogi su mi slali ispravke i svima sam dužnik, ali posebnu zahvalnost zaslužuju (za prvo
izdanje): Kevin Raulerson (koji je pronašao obilje sjajnih bubica), Bob Resendes (zaista ne-
verovatan), (ohn Pinto, Joe Dante, Joe Sharp (sva trojica su bili ćudesni), David Combs
(tnnoge gramatičke ispravke i razjašnjenja), 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 uložio je veliki
n ap o rd a u Evropi objavi i učitii dostupnom elektronsku verziju prvog izdanja knjige.
Hvala svitna koji su mi pomogli da ponovo napišem prim ere uz korišćenje biblioteke
Swing (za 2. izđanje) 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 podršku.
U 4. izdanju, Chris G rinđstaff 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 naučio o program iranju za paraleleno izvrša-
vanje, otvore se neka nova vrata i pokaže se da im a još planina koje m oram preći. Hvala
Brianu Goetzu što mi je pom ogao da savladam sve prepreke u novoj verziji poglavlja Pa-
ralelno izvršavanje i što je otkrio (nadam se!) sve greške.
Nije iznenađenje što mi je poznavanje Delphija pom oglo da razum em Javu, budući da
ta dva jezika imaju puno zajedničkih koncepata i dizajnerskih rešenja. Moji prijatelji koji
se bave Delphijem pom ogli su mi da proniknem u unutrašnjost tog sjajnog programskog
okruženja. 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 računare) i, naravno, Zack Urlocker (prvi direktor
projekta Delphi), stari prijatelj s kojim sam proputovao svet. Svi mi sm o dužnici briljant-
nom A ndersu Hejlsbergu, koji se i dalje muči 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 pružio mi je vrlo korisnu podršku (i Kim
takođe). Richard i ja smo proveli više meseci zajedno, držeći seminare, pokušavajući da
osm islim o savršen m etod učenja 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 računari i stono izđavaštvo budu otkriveni, i koji
se žalio na moje gunđanje zbog algebarskih problem a. Ipak, knjigu sam složio sam tako
da sve greške u slaganju idu na moj račun. 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 Južnoj
Africi, a drugo iz Praga (što je moj doprinos eri elektronike). Treće i četvrto sam poslao iz
Cresteđ Butta u Koloradu. Font korišćen na koricam a je ITC Rennie Mackintosh.
Posebno zahvaljujem svojim učiteljima i svim svojim studentim a (koji su istovremeno
i moji učitelji).
Dok sam radio na ovom izdanju, u krilu mi je često ležala mačka Mollv i tako davala
svoju toplu, krznenu podršku.
Listu prijatelja koji su me podržavali ć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“ —Oslobođeni Pro-
metej, Šeli

Ljudska bića... sasvim su pod vlašću određenog jezika koji je postao sredstvo izražavanja u nji-
hovotn društvu. U zabludi je ko misli da u prilagodavanju pojedinca stvarnosti jezik ne igra
znaćajnu ulogu i da je jezik samo slućajno sredstvo rešavanja specifićnih problema opštenja i
mišljenja. Činjenica je da je „stvarni svet“ u velikoj meri nesvesno izgraden na osnovu jezičkih
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


usložnjavanjem problem a, ovaj način izražavanja biće vam znatno lakši i fleksibilniji od
bilo kojeg drugog, ukoliko bude uspešan.
Ne m ožete gledati na Javu samo kao na gom ilu mogućnosti, jer neke m ogućnosti ne-
maju smisla same za sebe. Celinu svih delova možete da 'koristite sam o ako razmišijate o
projektovanju, a ne o pisanju koda. Da biste razumeli Javu na ovaj način, m orate razumeti
i problem e koji se pri tom e javljaju, kao i pri program iranju uopšte. Ova knjiga razm atra
problem e pri program iranju, objašnjava zbogčega oni predstavljaju problem e i pokazuje
postupke kojima ih Java rešava. Zato se skup m ogućnosti koje objaŠnjavam u svakom po-
glavlju zasniva na načinu rešavanja pojedine vrste problem a pom oću Jave. Na taj način,
trudim se da vas dovedem, korak po korak, do tačke kada Java postaje vaš m aternji jezik.
Od početka do kraja, moj stav počiva na tom e da vi želite sebi da predoćite model koji
će vas dovesti do dubokog razumevanja jezika; ako naiđete na zagonetku, moći ćete da je
ubacite u svoj model i pronađete 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 naućiti 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 dosadašnjem programiranju ovladali osnovnim
idejama, moći ćete da radite po ovoj knjizi. Naravno, kniiga će biti lakša C programerima,
i još više 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 možete preuzeti s Iokacije mvw.MiudView.net, ovladaćete u potpunosti
osnovama potrebnim za ućenje 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. Trudiću se da ta po-
ređenja budu jednostavna i objasniću svešto mislim da ne zna neko ko ne koristi C/C++.
Uvod 9

Učenje Jave
O tprilike u isto vreme kada se pojavila moja prva knjiga, Using C++ (Osborne/McGravv-
Hill, 1989), počeo sam da predajem taj jezik. Predavanje program skih jezika postala je
m oja profesija: gledao sam glave koje klimaju, bele pogleđe i zbunjene izraze u publici ši-
rom sveta još od 1987. Kada sam počeo privatno da obučavam m anje grupe Ijudi, tokom
vežbi sam otkrio da mnoga pitanja zbunjuju čak i one koji su se smešili i klimali glavom.
Više 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-
sečnoj publici serviramo p reV iše informacija za kratko vreme. Zbog različitih nivoa
publike i načina na koji sam predstavljao materijal, na kraju bih izgubio deo slušalaca.
M ožda previše tražim , ali pošto osećam otpor prem a tradicionalnom načinu predavanja
(a verujem da većina slušalaca takav otpor oseća zbog dosade), pokušao sam da učinim da
m oja predavanja nikom e ne budu dosadna.
Jedno vreme sam pravio dosta raznih prezentacija u relativno kratkom periodu. Tako
sam došao u situaciju da učim „m etodom pokušaja i pogrešaka" (što je dobar način i za
projektovanje program a). Na kraju sam iskoristio sve što sam naučio kao predavač- i na-
pravio kurs koji bih rado držao duže vreme. Sada ovaj kurs držim na javnim i privatnim
sem inarim a o Javi; to je moj glavni uvodni seminar, koji pruža osnovu za naprednije se-
m inare. O tim sem inarim a možete saznati više 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 ažu mi da m enjam i pre-
rađujem materijal sve dok nc zakliučim da je podesan kao sredstvo poučavanja. Ovu knji-
gu ne čine samo beleške sa seminara: u nju sam pokušao da spakujem što više inform acija
i spojio sam ih tako da vas vode od jednog do drugog predmeta. Zamislio sam da ova knji-
ga služi 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 uče jezik Java. Poglavlje u knjizi zamišljao sam kao jednu dobru lek-
ciju na sem inaru. Povratne intormacije od slušalaca na sem inarim a pom ogle su mi da
otkrijem koji su delovi teži i zahtevaju dodatna objašnjenja. U oblastim a gde sam bio pre-
am biciozan i uvrstio previše novih pojmova odjednom , spoznao sam - kroz proces pri-
kaza materijala - da više novih pojmova zahteva više objašnjavanja, a to lako zbunjuje
slušaoca.
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 način možete da prihvatite svaki đelić
teorije, u zavisnosti od vašeg dotadašnjeg znanja, pre nego što produžite 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 pažljivo odaberem redosled pojmova koje objaš-
njavam, tako da ne naiđete na nešto s čim se ranije niste susreli. Naravno, to nije
uvek moguće; u takvim situacijama, dat je kratak uvodni opis.
2. Da koristim što jednostavnije i kraće primere. To me ponekad sprečava da se uhva-
tim u koštac sa ,,stvarnim“ problem im a, ali sam prim etio da su početnici obično
srećniji kada mogu da shvate svaki detalj prim era, i nisu im presionirani obim om
problem a koji se rešavaju. Takođe postoji ozbiljno ograničenje količine koda koji
m ože da se razum e u učionici. O ni kojima se to ne dopada, neka ga prihvate kao
pedagoški ispravniji pristup.
3. Da pružim 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 neće imati potrebe da zna, a koje samo zbunjuju i povećavaju složenost je-
zika. Da uzm em prim er iz C-a: ako zapam tite tabelu prioriteta operatora (ja ni-
kada nisam), možete da pišete efikasan, ali nečitljiv kod. Ali ako malo promislite o
takvom stilu pisanja, shvatićete da zbunjuje onoga ko čita/održava kod. Stoga za-
boravite na prioritete i koristite zagrade svuda gde stvari nisu potpuno jasne.
4. Da svaki odeljak bude đovoljno usredsređen, tako da vreme izlaganja i pauza izme-
đu iziaganja i vežbe budu kratki. To slušaoca drži budnim i aktivnim tokom semi-
nara, a čitaocu pruža osećanje da brže ovladava materijom .
5. Da vam dam čvrst temelj, kako biste mogli dobro da razum ete teoriju i đa sami na-
stavite da proučavate Javu.

Poučavanje 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 više mogućnosti i biblioteka, ja sam tvrdoglavo nastojao da sve to ispre-
đajem za sedmicu dana. U jednom trenutku, neki naručilac je od mene zatražio da pre-
dajem „sam o osnove“, i radeći to otkrio sam da je sabijanje svega u jednu sedmicu postalo
mućno i za mene i za polaznike. Java više nije bila „jednostavan" jc/.ik koji se može ispre-
davati za nedelju dana.
To iskustvo i spoznaja potakli su me da reorganizujem knjigu, koja je sada napisana
tako da može da se ispredaje za dve sedmice na sem inaru ili dva semestra na fakultetu.
Uvodni deo se završava s poglavljem Obrada grešakn pomoću izuzetnkn, čenut biste mogli
da dodate uvod u JDBC, servlete i serverske Java stranice. To saćinjava 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 možete kupiti na w w w .M ind\ricw.nct.
Ukoliko vam trebaju informacije o dodatnom materijalu za predavaće, 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 možete pristupati koristeći čitač We-
ba; gotovo svaka druga nezavisna distribucija Jave ima takav ili sličan sistem dokum enta-
cije. U većini knjiga o Javi kopira se taj sistem. Stoga takvu dokum entaciju ili već imate,
ili je m ožete preuzeti s Weba, i ukoliko to zaista nije neophodno, u ovoj knjizi je nećemo
ponavljati. O bično m nogo brže možete da nađete opis neke klase pom oću čitača Weba,
nego da ga tražite po knjizi (a dokum entacija na Webu je verovatno ažurnija). Jednostav-
no ću vas uputiti na„dokum entaciju JDK-a“. O vaknjiga će obezbediti dodatne opise klasa
samo kada je neophodno dopuniti dokum entaciju da biste shvatili odredeni prim er.

Vežbe
Prim etio sam da jednostavne vežbe pom ažu studentim a da shvate seminarsko gradivo, pa
dajem nekoliko vežbi na kraju svakog poglavlja.
Veći broj vežbi može relativno lako i u razum nom roku da se uradi u učionici, pod
nadzorom instruktora koji proverava da li su svi studenti usvojili izloženu m ateriju. Neke
vežbe su malo izazovnije, a li nijedna nije nerešiva.
Rešenja izabranih vežbi nalaze se u elektronskom dokum entu pod im enom Thc Thin-
king iu java Annotatcd Solution Guide, koji se može kupiti na lokaciji www.Mind-
Vicw.com.

Javini temelji
Sa ovom knjigom dobijate i besplatan m ultim edijalni sem inar koji možete 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 može besplatno
preuzeti.
Prvobitno sam od Chucka Allisona naručio da napravi Thinking in C k ao nezavisan
proi/.vod, aii sam odlućio da ga priložim uz drugo izdanje knjige Thinkingin C++ i drugo
i treće izdanje knjige Thinking in Java, pošto 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 neću da ućim C, nego C ++ ili Javu, pa ću C preskočiti i odm ah preći na C + + /
Javu“. Po dolasku na seminar, ljuđi polako shvataju da sam imao veoma dobar razlog što
sam poznavanje C-a proglasio za preduslov za pohađanje.
Tehnologije se menjaju, i bilo je bolje preraditi Thinking in C u Flash prezentaciju koja
se može preuzeti sa Interneta, nego prilagati ga na CD-u. Pošto je taj seminar na Inter-
netu, svi mogu dobro da se priprem e prc nego što dođu na moj seminar.
Sem inar Thinking in C i ovoj knjizi pribavlja veću publiku. Iako njena poglavlja Ope-
ralori i Kontrolisanjc izvršavanja obuhvataju osnove Jave koje potiču iz C-a, mrežni 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 zaštićen autorskim
pravom, u jednom paketu, na adresi www.MindView.net. Ovo je zvanična Web lokacija za
distribuciju koda i elektronskog izdanja knjige, pa m ožete biti sigurni da ćete tu uvek naći
najnoviju verziju. Kod m ožete deliti i koristiti u učionicam a i u druge obrazovne nam ene.
Osnovni cilj zadržavanja autorskog prava jeste da se poreklo koda ispravno navede i da
se spreči objavljivanje koda u štam panim medijim a bez dozvole. (Ukoliko je poreklo koda
ispravno navedeno, prim eri iz knjige m ogu se navoditi u većini medija.)
U svakoj datoteci sa izvornim kodom naći ćete sledeću napom enu o autorskim pravi-
ma (obično na engleskom):

//:! A u t o r s k a P r a v a . t x t
Ovaj raču 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 zadržana.

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


uključite u privatne i komercijalne softverske programe, ali samo u izvršnom
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, uključujući 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 jamči da će b i l o koji p r o g r a m koji sadrži 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 istraživanja 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 , POSLEDIČNU 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, UKLJUČUJUĆI I I M P L I C I T N U G A R A N C I J U 0 PR O D A J I 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 DRŠKE,
AŽURIRANJA, POBOLJŠANJA 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. održava 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
pošaljete preko sistema povratnih informacija koji m o ž e t e pr onaći na lo ka ci ji
www.MindView.com.

///:-
Kod možete koristiti u svojim nastavnim projektim a (uključujući i vaše materijale za
prezentaciju), dokle god zadržavate poruku o autorskim pravim a koja se pojavljuje u sva-
koj izvornoj datoteci.

Način pisanja korišćen u knjizi


U ovoj knjizi identifikatori (metode, prom enljive i im ena klasa) ispisani su polucrno. Ve-
ćina rezervisanih reči takode je napisana polucrno, osim onih koje se toliko koriste da bi
njihovo naglašavanje postalo zam orno.
Za pisanje prim era u knjizi koristim odreden stil. On odgovara stilu koji se koristi u
kom paniji Sun za praktično sve program e koje možete naći na njihovoj Web Iokaciji (po-
gledajte http://jiiva.siin.coin/docs/codcconr/indcx.html) i koji podržava većina razvojnih
okruženja za favu. Ako ste čitali moje druge radove, prim etili ste takođe da se stil pisanja
program a koji koristi Sun pođudara s m ojim stilom. Ovo mi je drago, iako ja s tim e (ko-
liko znam ) nisam imao nikakve veze. Pitanje stila može biti predm et višečasovnih raspra-
va, pa ću samo reći da kroz moje prim ere ne želim da nam ećem pravilan stil, nego imam
Iične razloge za korišćenje takvog stila. Pošto je Java program ski jezik slobodne forme,
možete da koristite bilo koji stil koji vam ođgovara. Da biste form atiranje doterali kako
vam odgovara i rešili pitanje stila, možete se poslužiti alatkom kao što je Jalopy (www.trie-
m ax.com ) —dok sam pisao knjigu, koristio sam je.
Programi u ovoj knjizi ubačeni su u tekst direktno iz datoteka koje su prevođene
i ispitivane na jednom autom atskom sistemu. Stoga bi izvorni kod štam pan u knjizi tre-
balo da radi bez grešaka pri prevođenju.
14 Misliti na Javi

Ova knjiga se zasniva na Javi SE5/6, i na njoj je i testirana. Ako hoćete da naučite nešto
o ranijim varijantama jezika, a to nije obuhvaćeno ovim izdanjem, prvo, drugo i treće iz-
danje knjige (na engleskom) možete besplatno preuzeti s lokacije www.MindView.net.

Greške
Bez obzira na to koliko trikova pisac koristi da otkrije greške, neke se uvek provuku, a nov
čitalac ih često odm ah primeti. Ako otkrijete bilo šta što sm atrate greškom, m olim vas da
iskoristite hipervezu za ovu knjigu na lokaciji www.MindView.net, prijavite grešku i
pošaljete predlog za ispravku. Ceniću vašu pomoć.
Upoznavanje sa objektima
„Prirodu parcelišemo i razvrstavamo u koncepte kojima pripisujemo značenja, uglavnom
zato što se držimo dogovora koji važi u našoj jezičkoj zajednici i kodifikovan je u obrascima
našeg jczika ... uopštc nc možetnogovoriti ukoliko se nepridržavamo 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 mašina.
Ali raćunari nisu toliko mašine koliko pojačala um a („točkovi za um “ kako Steve Jobs
voli da kaže) i drugi način izražavanja. Zbog toga ti alati sve m anje liče na mašine, a sve
više na delove našeg uma, i na druge oblike izražavanja, kakvi su pisanje, slikanje, vajanje,
anim iranje i snim anje filmova. O bjektno orijentisano program iranje (OOP) deo je ovog
pom eranja ka korišćenju računara kao sredstva izražavanja.
Ovo poglavlje vas uvodi u osnovne koncepte OOP-a, uključivši 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
koštac sa ovom knjigom, proučite multimedijski sem inar Thinking in C, koji možete
preuzeti na adresi mvw.MitidView.net.
Ovo pogtavlje je i podloga i dodatni materijal. M nogi se ne osećaju 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-
fićnosti jezika, slobodno preskoćite ovo poglavlje - u ovom trenutku to vas neće sprečiti
da pišete program e ili naučite jezik. M eđutim, poželećete da se vratite na ovaj deo knjige
da biste dopunili svoje znanje i shvatili zašto su objekti važni i kako da ih koristite pri
pisanju programa.

Razvoj apstrakcije
Svi programski jezici obezbeđuju apstrakoiju. Moglo bi se raspravljati o tom e da li je slo-
ženost problema koje ste u stanju da rešite direktno povezana s vrstom i kvalitetom ap-
strakcije. Pod „vrstom" mislim na to „šta apstrahujete“. Mašinski jezik je mala apstrakcija
mašine na kojoj se programi izvršavaju. Mnogi takozvani ,,proceduralni“ jezici koji su sle-
dili mašinski (kao FORTRAN, RASIC i C) bili su apstrakcija mašinskog jezika. Ovi jezici
su veliki napredak u odnosu na mašinski jezik, ali njihova prim arna apstrakcija ipak za-
hteva od vas da razmišljate iz ugla strukture računara um esto iz ugla probiem a koji reša-
vate. Program er mora da uspostavi vezu izmedu modela tnašine (u „prostoru rešenja“
koji predstavlja mesto gde realizujete rešenje problem a, na prim er, u računaru) i modela
problem a koji se rešava (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 teško pišu i čije je održavanje
skupo, a kao sporedni elekat nastaje celokupna industrija „program skih metoda".
16 Misliti na Javi

Alternativa m odelovanju m ašine je m odelovanje problem a koji pokušavate da rešite.


Rani jezici kao LISP i APL odražavali 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 odlučivanja. Stvoreni su jezici zasnovani na ograničenom program i-
ranju i program iranju isključivo m anipulacijom grafičkim sim bolim a (što se pokazalo
kao previše restriktivno). Svaki ovaj p ristu p može biti dobro rešenje za određenu klasu
problem a kojoj su nam enjeni, ali kada istupite iz tog dom ena, oni postaju nezgrapni.
O bjektno orijentisani pristup ide korak dalje, obezbeđujući alate pom oću kojih progra-
m er predstavlja elemente u prostoru problem a. Ovo predstavljanje u principu ne ogra-
ničava program era na jednu vrstu problem a. Elemente u prostoru problem a i njihovo
predstavljanje u prostoru rešenja nazivamo ,,objekti“. (Trebaće 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 rešenje, u isto vreme čitate i reči koje izražavaju problem . Ovo je mnogo flek-
sibilnija i snažnija apstrakcija od pretho d ne.1Stoga O O P dozvoljava da opišete problem iz
ugla problem a, um esto iz ugla računara na kom e će se to rešenje izvršavati. loš uvek postoji
povratna veza ka računaru: svaki objekat izgleda posve kao mali računar - on ima unu-
trašnje stanje i operacije koje m ožete zahtevati da izvrši. Međutim, ovo i nije tako loša ana-
logija sa objektima u stvarnom svetu - svi im aju svoje karakteristike i osobeno se ponašaju.
AJan Kay je naveo pet osnovnih obeležja Smalltalka, prvog uspešnog objektno orijen-
tisanog jezika, i jednog od jezika na kom e je Java zasnovana. Ta obeležja predstavljaju čist
i neiskvaren pristup objektno orijentisanom program iranju.
1. Sve je objekat. Posmatrajte objekat kao poboljšanu promenljivu; on čuva podatke,
ali možete i da mu „postavite zahteve“ koje ispunjava vršeći operacije nad tim po-
dacima. Teoretski, možete uzeti bilo koju idejnu kom ponentu problem a koji reša-
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 saopštavaju š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 određenom
objektu.
3. Svaki objekat im a svoj m em orijski p ro sto r koji se sastoji od d ru g ih objekata.
Drugačije rečeno, vi stvarate novu vrstu objekta praveći paket koji sadrži neke po-
stojeće objekte. Stoga možete da usložnjavate program koji će biti skriven iza jed-
nostavnih objekata.
4. Svaki objekat ima tip. Stručno rečeno, svaki objekat je instanca (primcrak) neke
klase, pri čem u su ,,klasa“ i ,,tip“ sinonim i. Najvažnija odlika klase glasi: „Koje poru-
ke joj možete 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 l’ro g rtunm ing in Lctlti, Ti-
m o th y B u d d (A d d iso n -W e sle y 1995).
18 Misliti na Javi

Pošto klasa opisuje skup objekata koji imaju identićne karakteristike (elementi s po-
dacima) i jednako se ponašaju (funkcionalnost), klasa ie zaista tip podatka, jer i broj u
form atu pokretnog zareza, na prim er, takođe im a skup karakteristika i ponašanja. Razlika
je u tom e što program er definiše klasu koja odgovara problem u, um esto da bude prinu-
đen da koristi postojeći tip koji predstavlja m em orijsku jedinicu unutar računara. Pro-
gramski jezik proširujete dodavanjem novih tipova podatka, specifičnih za vaše potrebe.
Programski sistem prihvata nove klase, o njim a vodi računa i proverava tipove, kao što
radi i sa ugrađenim tipovima.
O bjektno orijentisan pristup ne ograničava se sam o na pravljenje simulacija. Bez ob-
zira na to da li se slažete sa stavom da je svaki program simulacija sistema koji projektu-
jete, upotrebom tehnika, O O P-a veliki skup problem a lako se može da svesti na
jednostavna rešenja.
Kada se klasa ustanovi, možete da napravite koliko god želite objekata te klase, a zatim
da radite s tim objektim a kao da su elementi u problem a koji pokušavate da rešite. Zaista,
jedan od izazova objektno orijentisanog program iranja jeste ostvarivanje jednoznačnog
preslikavanja izm eđu elemenata u prostoru problem a i objekata u prostoru rešenja.
Kako da naterate objekat da uradi koristan posao? M ora postojati način da postavite
zahtev objektu da nešto uradi, na prim er, da završi transakciju, iscrta nešto na ekranu ili
uključi prekidač. A svaki objekat može da zadovolji samo izvesne zahteve. Zahtevi koje
možete da postavite objektu đefinisani su preko njegovog interfcjsa, koji je određen
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 možete da postavite odredenom objektu. Naravno, ne


gde m ora postojati kod koji će zađovoljiti taj zahtev. 'Iaj kod, zajedno sa skrivenim poda-
cima, ćini implementaciju (realizaciju ili prim enu interfejsa). S taćke gleđišta
proceduralnog program iranja to i nije tako komplikovano. Tip uz svaki mogući zahtev
ima pridruženu m etodu i kada napravite ođređeni zahtev, poziva se odgovarajuća meto-
da. Za ovaj proces obično se kaže da sm o „poslali poruku" (postavili zahtev) objektu, a
objekat odreduje šta će s porukom da uradi (izvrši kod).
U našem prim eru, ime tipa/klase je Sijalica, ime posebnog objekta tipa Sijalica je sj, a
zahtevi koje m ožem o postaviti objektu Sijalica su uključi se, isključi se, pojačaj svetlo ili
priguši svetlo. Objekat tipa Sijalica pravite kada definišete 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 povežite ga s porukom - zahtevom, razdvajajući ih tačkom. S tačke
gledišta 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 pruža usluge


D ok pokušavate da razvijete ili shvatite stru kturu nekog program a, bilo bi dobro da ob-
jekte sm atrate „davaocima usluga“. I vaš program će pružati usluge korisniku, i to po-
m oću usluga koje pružaju drugi objekti. Vaš cilj je da napravite (ili još bolje, pronađete u
bibliotekam a koda) skup objekata koji pružaju idealne usluge koje rešavaju vaš problem.
To biste mogli postići ako se zapitate: ,,Da ih mogu čarobnim štapićem izvući iz šešira,
koji objekti bi odm ah rešili moj problem?" Na primer, pretpostavim o da pišete program
za knjigovodstvo. Mogli biste zamisliti ođređene objekte koji sadrže unapred definisane
slike ekrana za unos podataka, drugi skup objekata koji obavljaju knjigovodstvene
proračune, i objekat koji štam pa čekove i račune na svirn mogućim vrstam a štampača.
Neki od tih objekata m ožda već postoje? Kako bi izgledali oni koji ne postoje? Koje usluge
bi ti objekti davali i koji bi im objekti bili potrebni za izvršavanje zadataka? Ako tako bu-
dete radili, doći ćete do taćke kada možete reći: „Ovaj objekat izgleda dovoljno jednostav-
no da se može napisati“ ili „Mora da postoji ovakav objekat“. To je racionalan naćin
razlaganja problem a na skup objekata.
Kada se objekat sm atra davaocem usluga, stiće se još jedna prednost: time se povećava
usklađenost objekta. Velika u$kladenost\e jedan od temeljnih kvaliteta projektovanja soft-
vera: to /.naći da su razni aspekti softverske kom ponente (kao što je objekat, iako isto važi
i za m ctodu iii biblioteku objekata) m edusobno „dobro uklopIjeni“. Prilikom pro-
jektovanja objekata, program eri ćesto trpaju previše funkcionalnosti u jedan objekat.
Kada se radi o pom enutom m odulu za štam panje ćeko\ra, možete odlučiti da vam treba
objekat koji zna sve o form atiranju i štam panju. Verovatno će vas iskustvo naućiti da je to
previše za jedan objekat i da vam treba tri ili višeobjekata. Jedan olijekat bi mogao biti ka-
talog svih mogućih izgleda čekova, kojem se mogu slati upiti radi podataka o tom e kako
odštam pati odredeni ćek. Jedan objekat ili skup objekata može biti opšti (generički) in-
terfejs za štam panje, koji zna sve o raznim vrstam a štampača (ali ništa o knjigovodstvu
- taj je kandidat za kupovinu, um esto da ga sami pišete). A treći objekat može koristiti
usluge prethodna dva da bi obavio posao. Tako bi svaki objekat imao uskladen skup uslu-
ga koje pruža. U dobrom objektno orijentisanom dizajnu, svaki objekat dobro radi jedan
posao, ali ne pokušava da radi više poslova. Ne samo da se tako mogu pronaći objekti koje
treba kupiti (recimo, objekat interfejsa štampača), 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 pokuša-
vali da razum eju vaš kod ili upotrebe neki od vaših objekata. Ako b u d u mogli da utvrde
vrednost objekta na osnovu usluge koje on pruža, biće im m nogo lakše 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 drži sakriveno. Zašto? P rogram er Idijent ne
može da koristi sakrivene delove, što znači da autor klase m ože da prom eni skriveni deo
kad god hoće, ne razmišljajući da li će se to odraziti na ostale. Skriveni deo obično pred-
stavlja osetljivu unutrašnjost objekta koju lako može da ošteti neobazriv ili neobavešten
program er ldijent, pa sakrivanje realizacije sm anjuje m ogućnost da se pojave greške u
program im a.
U svakom odnosu je važno da sve uključene strane poštuju granice. Kada pravite bi-
blioteku, vi stvarate odnos sa kJijentom koji je takođe program er, ali koji sastavlja aplika-
ciju koristeći vašu biblioteku, da bi, možda, napravio veću 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 način da se nam etnu pravila. Jako biste vi voleli da
program er Jdijent ne radi direktno s nelcim članicama vaše klase, bez kontrole pristupa ne
bi postojao način da to sprečite. Sve bi bilo izloženo javnosti.
Stoga je prvi razlog za uvođenje kontrole pristupa onem ogućavanje 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 rešavanje njihovih
problem a. Ovo je u isto vreme usluga program erim a klijentima, jer lako rnogu da razluče
šta je za njih važno, a o čemu ne treba da misle.
Kontrolu pristupa treba uvesti i da bi se dozvolilo projektantu biblioteke da prom eni
način na koji klasa interno radi, a da ne m ora da brine kako će se to odraziti na progra-
mere klijente. Na primer, možete realizovati neku klasu na jednostavan način, a kasnije
otkriti da je treba ponovo napisati kako bi radila brže. Ako su interfejs i realizacija jasno
razdvojeni i zaštićeni, ovo možete lako da izvedete.
Java koristi tri rezervisane reči da postavi granice un u tar klase: public, private i pro-
tected. Ovi specifikatoripristupaodređuju ko može da koristi definicije koje slede iza njih.
public (javni) znači da je naredni elem ent dostupan svakome. Rezervisana reč private
(privatni) znači da tom elementu ne može da pristupi niko sem autora klase, unutar me
toda tog tipa. private predstavlja zid između autora i program era klijenta. Onaj ko poku
ša da pristupi članici označenoj kao private, izazvaće grešku 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 (zaštićeni) deluje kao private, s tom razlikom što klase nasled-
nice im aju pristup zaštićenim članicama, ali ne i privatnim članicama. O nasleđivanju
ćemo govoriti uskoro.
Java ima i ,,podrazum evani“ pristup koji se koristi ako ne zadate nijedan od navedenih
specifikatora. Ovo se obično 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 korišćenje realizacije


Kada se klasa napravi i testira, ona bi (u idealnom slučaju) trebalo da predstavlja korisnu
jedinicu koda. Ispostavlja se da ponovno korišćenje ni izbliza nije tako lako ostvarljivo
kako se mnogi nadaju; potrebno je iskustvo i pronicljivost da bi se napisao višekratno
upotrebljiv objekat. Ali kada ga napišete, on moli da bude ponovo iskorišćen. Ponovno ko-
rišćenje koda je jedna od najvećih prednosti objektno orijentisanih program skih jezika.
Najjednostavniji način da ponovo iskoristite klasu jeste da direktno koristite objekat te
klase; m eđutim , objekat te klase možete da stavite i u novu klasu. Ovo se naziva „pravlje-
nje objekta člana“ (engl. member object). Vaša nova klasa m ože 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 dešava dinam ički, onda obično agregacija).
Kompozicija se često poredi s relacijom ,,ima“, na p rim er„au to im a m o to r“.

(Na ovom UML dijagram u kompoziciju označava popunjen romb, koji kazuje da po-
stoji jedan auto. Kad označavam spajanje, obično ću koristiti jednostavniji oblik: sam o li-
niju, bez rom ba.)1
Kompozicija je vrlo fleksibilna. Objekti članovi vaše nove klase obično su privatni, što
ih čini nedostupnim program erim a Idijentima koji koriste klasu. Ovo omogućava da iz-
menite te članove ne remeteći postojeći klijentski kod. Možete m enjati objekte članove i
u vreme izvršavanja da biste dinamički menjali ponašanje svog program a. Nasleđivanje
koje malo dalje opisujemo, nem a ovu fleksibilnost pošto prevodilac m ora da ugradi ogra-
ničenja za prevođenje u klase stvorene nasleđivanjem.
Pošto je nasledivanje veoma bitno, u objektno orijentisanom program iranju često se
veoma naglašava, i nov program er može pom isliti da nasleđivanje treba koristiti svuda.
Kaii rezultat može se dobiti veoma nezgrapan i veoma složen program . Umesto toga, prvo
treba videti da li pri pravljenju novih klasa može da se iskoristi kompozicija, pošto je ona
jednostavnija i fleksibilnija. Ako prim enite ovaj pristup, program će biti čistiji. Kada stek-
nete nešto iskustva, biće vam očigledno kada treba da koristite nasleđivanje.

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

Nasleđivanje
Sam po sebi, koncept objekta je veoma koristan. On omogućava da podatke i funkcional-
nost grupišete po konceptu, tako da možete pređstaviti odgovarajuću ideju u prostoru
problem a, um esto da budete prinuđeni da koristite izraze računara na kome radite. Kada
se prim eni rezervisana reč class, te ideje su izražene kao osnovne jedinice u ovom pro-
gram skom jeziku.
Bila bi šteta da se nam učite i napravite neku klasu a da onda budete prim orani da pra-
vite p otpuno novu klasu koja im a sličnu funkcionalnost. Bilo bi jednostavnije da uzme-
m o postojeću klasu, da je kloniram o, a zatim dopunjujem o i m enjam o kloniranu klasu.
Ovo se zapravo postiže nasleđivanjem (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 nasleđena 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, može da postoji više izvedenih klasa.)
Tip ne označava samo ograničenja skupa objekata; on ima odnose i s drugim tipovi-
ma. Dva tipa m ogu imati neke zajedničke karakteristike i jednako se ponašati, ali pri tom
jedan tip može imati još neke dodatne karakteristike i može obrađivati više poruka (ili ih
drugačije obradivati). Kod nasleđivanja, ova sličnost između tipova se izražava preko
koncepta osnovnih i izvedenih tipova. Osnovni tip sadrži sve karakteristike i ponašanja
koja su zajednička za tipove izvedene iz njega. Praveći osnovni tip, izražavate suštinu svo-
jih ideja o nekim objektim a u sistemu. Iz osnovnog tipa izvodite ostale tipove i pokazujete
različite načine realizacije ove suštine.
Na prim er, mašina za recikliranje raspoređuje komadiće otpada. Osnovni tip je ,,ot-
pad “ a svaki pojedini otpadak ima težinu, vrednost itd. i može biti isečen, istopljen ili ra-
stavljen. Iz osnovnog tipa izvodimo posebne vrste otpada s dodatnim karakteristikama
(boca im a boju), ili ponašanjim a (alum inijum ska konzerva može da se zdrobi, čelične
konzerve privlači magnet). Ponašanja mogu biti različita (vrednost papira zavisi od nje-
gove vrste i stanja). Koristeći nasledivanje možete izgracliti hijerarhiju tipova. Ta hijerar-
hija opisuje problem koji pokušavate da rešile preko tipova koji se u problem u pojavljuju.
Drugi prim er je klasičan ,,oblik“ koji može 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
veličinu, boju, poziciju itd. Svaki oblik može da se iscrta, obriše, 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 može imati dodatne karakteristike i ponašanje. Neki oblici, na prim er, m ogu
da se okreću. Neka ponašanja m ogu biti drugačija, recimo kada želite da izračunate po-
vršinu oblika. H ijerarhija tipova objedinjuje sličnosti i razlike između oblika.

Predstavljanje rešenja i problem a u istom obliku veoma je korisno jer vam ne treba ve-
liki broj m eđum odela da biste od opisa problem a došli do opisa rešenja. Kod objekata, hi-
jerarhija tipova je osnovni model tako da sa opisa sistema u realnom svetu direktno
prelazite na opis sistema kodom. Jedna od teškoča sa objektno orijentisanim program i-
ranjem jeste ta da je previše jednostavno stići od početka od kraja. Um treniran da traži
složena rešenja često spočetka zbunjuje ova jednostavnost.
Kada iskoristite nasleđivanje iz postojećeg tipa, stvarate novi tip. Taj novi tip ne samo
da sađrži sve članice postojećeg tipa (iako su privatne članice sakrivene i neđostupne),
već, što je mnogo važnije, kopira interfejs osnovne klase. Znači, sve poruke koje možete
da pošaljete objektima osnovne klase, takođe možete poslati i objektim a izvedene klase.
Pošto se tip klase odreduje na osnovu poruka koje joj m ožem o poslati, to znači 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.
Pošto i osnovna klasa i izvedena klasa imaju isti osnovni interfejs, m ora da postoji rea-
lizacija koja ide uz taj interfejs. Znači, mora postojati kod koji se izvršava kada objekat
prim i određenu poruku. Ako nasledite klasu i ništa više ne uradite, m etode interfejsa
osnovne klase će preći i u izvedenu klasu. To znači da objekti izvedene klase imaju i isti tip
i isto ponašanje, što i nije previše korisno.
Postoje dva načina da napravite raziiku između 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 znači da osnovna klasa nije radila sve ono što ste hteli, pa
ste joj zato dodali još m etoda. Ovaj jednostavan način korišćenja nasledivanja ponekad je
savršeno rešenje problem a. M edutim, pažljivo proučite da li su i vašoj 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 nasleđivanje ponekad može da nagovesti (posebno u Javi, gde rezervisana reč
koja označava nasledivanje glasi extends - proširuie) kako ćete dođavati nove m etode in-
terfejsu, to nije uvek istina. Drugi i važniji način da napravite razliku u novoj klasi jeste da
promcnite ponašanje postojeće 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


kažete: „Koristim ovde istu m etodu interfejsa, ali želim da ona u m om novom tipu radi
nešto drugo.1'
Poglav[je 1: Upoznavanje sa objektima 25

Relacije JE i JE-KAO
Može se postaviti jedno pitanje o nasledivanju: ,,Da li nasleđivanje treba da redefiniše
samo m etode osnovne klase (i da ne dodaje nove m etode koje ne postoje u osnovnoj kla-
si)?“ To bi značilo da je izvedena klasa potpimo istog tipa kao osnovna klasa, jer ima isto-
vetan interfejs. Kao rezultat, objekat izvedene klase može se svesti na tip objekta osnovne
klase. Ovo je tzv. čista supstitucija, ili princip supstitucije. Unekoliko, ovo je idealan način
prim ene nasleđivanja. U ovom slučaju, m i se često pozivamo na relaciju izm eđu osnovne
i izvedene klasekao na relaciju je, jer m ožem o reći„krug;eoblik“. Pokušaj da utvrdim o da
li m ožem o da uspostavim o relaciju je izm edu klasa, a da to ima smisla, predstavlja svoje-
vrstan test nasleđivanja.
Ponekad m orate da dodate nove elemente u interfejs izvedenog tipa i tim e proširite
postojeći interfejs. Novi tip i dalje m ože biti sveden na osnovni tip, ali supstituđja nije sa-
vršena, jer nove m etode nisu dostupne iz osnovnog tipa. Ovo se može opisati kao relacija
je-kao (moj term in). Novi tip ima interfejs starog tipa ali sadrži i druge m etode, tako da
se ne može reći kako je potpuno isti. Uzmimo za prim er uređaj za klimatizaciju. Pretpo-
stavim o da su po kući razvedene kontrole za hlađenje, odnosno, kuća im a interfejs koji
om ogućava da kontrolišete hlađenje. Zamislite da se uređaj za klimatizaciju pokvari i da
ga zam enite toplotnom pum pom koja može i da greje i da hladi. Toplotna pum pa je kao
uređaj za hlađenje, ali ona može i više. Pošto je kontrolni sistem vaše kuće projektovan
sam o da kontroliše hlađenje, on je ograničen na kom unikaciju s delom za hlađenje novog
objekta. Interfejs novog objekta je proširen, a postojeći sistem ne poznaje ništa drugo
osim originalnog interfejsa.

Cim proučite ovaj plan, postaće jasno da osnovna klasa „sistem za hlađenje" nije do-
voljno opšta i da treba da se preim enuje u „sistem za kontrolu temperature", kako bi m o-
gla da sadrži i grejanje - nakon čega bi princip supstitucije važio. Ovaj dijagram prikazuje
šta u stvarnom svetu može da se desi tokom projektovanja.
Kada upoznate princip supstitucije, lako možete pomisliti da je taj pristup (čista sup-
stitucija) jedini način da se nešto uradi, i zaista jeste dobro da program napravite na taj
način. Ali shvatićete da u interfejs izvedene klase ponekad m orate da dodate nove metode.
Nakon što ispitate problem , trebalo bi da bude prilično očigledno o kojem se od ta dva
slučaja radi.
26 Misliti na Javi

Virtuelizacija objekata preko polimorfizma


Ukoliko imate posla s hijerarhijom tipova, često objekat ne tretirate kao specifičan tip, već
kao osnovni tip. To vam omogućava da pišete kod koji ne zavisi od specifičnog tipa. U pret-
hodnom prim eru sa oblicima, m etode m anipulišu generičkim tipovim a (oblicima) bez
obzira na to da li se radi o krugovima, kvadratima, trouglovim a ili čak obliđm a koji još
nisu definisani. Svi oblici mogu biti iscrtani, obrisani i pom erani, tako da te m etode samo
pošalju poruku objektu tipa oblik i ne brinu kako će objekat izaći na kraj s tom porukom.
Takav kod se ne menja ni nakon dodavanja novih tipova, a dodavanjem novih tipova
najčešće se proširuju objektno orijentisani program i kada treba da se savlada neka nova
situacija. Na primer, možete da izvedete novi podtip oblika, pod im enom petougao, bez
menjanja m etoda koje rade samo s generičkim oblicima. M ogućnost da se program lako
proširi izvođenjem novih podtipova jeste jedan od osnovnih načina da se kapsuliraju
promene. Time se znatno unapređuje program i istovrem eno sm anjuju troškovi održa-
vanja softvera.
Problem nastaje pri pokušaju da se objekti izvedenog tipa tretiraju kao generički
osnovni tipovi (krugovi kao oblici, bicikli kao vozila, korm orani kao ptice itd.). Ako me-
toda naredi generičkom obliku da se iscrta ili generičkom vozilu da sm ota volan ili gene-
ričkoj ptici da se pom eri, prevodilac prilikom prevođenja ne može tačno da zna koji deo
koda će biti izvršen. To i jeste poenta - kada je poruka poslata, program er ne želi da zna
koji deo koda će biti izvršen. M etoda za crtanje može podjednako da se prim eni na krug
ili na kvadrat ili na trougao, a objekat će izvršiti odgovarajući kođ u zavisnosti od svog
specifičnog tipa.
Ako ne m orate znati koji će deo koda biti izvršen, dodajte nov podtip; kod koji će taj
novi tip izvršavati može biti drugačiji a da pri tom ne m crate ništa da menjate u metodi
koja ga poziva. Znači, prevodilac ne zna tačno koji će deo koda biti izvršen. Šta onda radi?
Na primer, u sledećem dijagram u objekat K o ntrolerP tica radi samo s generičkim 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 tačno tipom Ptice radi, ili kakvo
je ponašanje te određene Ptice. Kako se događa da se, kada se pozove metoda pom eriSe()
uz ignorisanje specifičnog tipa Ptice, prim eni pravilno ponašanje (Guska hoda, leti ili
pliva, a Pingvin hoda ili pliva)?
Poglav[je 1: Upoznavanje sa objektima 27

Odgovor leži u osnovnoj začkoljici objektno orijentisanog program iranja: prevodilac


ne može da pozove funkciju na tradicionalan način. Prevodilac koji nije objektno orijen-
tisan izaziva rano vezivanje (engl. early binding). Ovaj term in m ožda niste ranije čuli, jer
o pozivanju funkcija niste razmišljali ni na koji drugi način. To znači da prevodilac gene-
riše poziv funkcije određenog im ena, a izvršni sistem kasnije razreši ovaj poziv ubaciva-
njem apsolutne ađrese koda koji treba da se izvrši. U OO P-u, program ne može da odredi
adresu koda sve do trenutka izvršavanja, tako da je neophodna neka druga šema kada se
poruka šalje generičkom objektu.
Da bi rešili ovaj problem , objektno orijentisani jezici koriste koncept kasnog vezivanja
(engl. late binding). Kada pošaljete poru ku objektu, kod koji se poziva ne biva određen
sve do trenutka izvršavanja. Prevodilac ipak proverava da li telo specifične m etode postoji
te koji su tipovi argum enata i povratne vrednosti, ali ne zna koji če kod izvršiti.
Da bi razrešila kasno povezivanje, um esto apsolutnog poziva Java koristi specijalan
delič koda koji adresu tela m etode izračunava pom oću inform acija ugrađenih u konkre-
tan objekat. (Ovaj proces je detaljno opisan u poglavlju o polim orfizm u.) Tako svaki
objekat može da se ponaša različito, u skladu sa sadržajem svakog pojedinog specijalnog
delića koda. Kada pošaljete poruk u objektu, on sam odlučuje šta će s njom da uradi.
U nekim jezicima m orate izričito navesti da određenu m etodu treba kasno povezati (u
jeziku C ++ to se radi pom oću rezervisane reči v irtu al). U tim jezicima, m etode se podra-
zum evano Hepovezuju dinamički. U Javi se dinam ičko povezivanje podrazum eva, tako
da ne m orate dodavati posebne rezervisane reči 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, napisaćemo naredbe koje ignorišu specifične detalje tipova i obraćaju se samo
osnovnoj klasi. Te naredbe nisii povezane sa inform acijam a specifičnim za pojedine tipo-
ve, pa se stoga jednostavnije pišu i lakše razumevaju. Ako pom oću nasleđivanja dodam o
nov tip - Sestougaonik, na prim er - te naredbe radiće jednako dobro s novim tipom O b-
lika kao što su radile sa već postojećim tipovim a. Znači, program je proširiv.
Alco napišete m etodu u Javi (što ćete uskoro naučiti):

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 obraća bilo kom O bliku, pa ne zavisi od specifičnog tipa objekta koji se is-
crtava i briše. 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 tačan tip objekta.
28 Misliti na Javi

Ovo je prilično zadivljujući trik. Posmatrajte red:

radiNesto(krug);

Ovde je Krug prosleden metodi koja očekuje Oblik. Pošto Krug jeste Oblik, funkcija
radiNesto() može tako da ga tretira. O dnosno, bilo koju poruku koju m etoda radiNe-
sto() m ože da pošalje Obliku, Krug može da prihvati. Zato je gornji poziv p o tp u n o sigu-
ran i logičan.
Proces pri kome izvedeni tip tretiram o kao osnovni tip, nazivamo svođenje naviše
(engl. npcasting). Ime castje iskorišćeno zato što označava ubacivanje u kalup (engl. ca-
sting into a tnold-ubacivan je u kalup), a up potiče od načina na koji se obično organizuje
dijagram nasleđivanja, sa osnovnim tipom na vrhu i izvedenim klasama koje se lepezasto
šire naniže. Prema tome, konverzija u osnovni tip je penjanje uz dijagram nasleđivania:
svođenje naviše ili upcasting.

U objektno orijentisanom program u uvek postoji svođenje naviše, jer pri tom ne mo-
rate znati tačan 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 pažnju na to da se nigde ne govori:,, Ako si Krug, radi ovo, ako si Kvadrat radi
onoitd." Tako napisan kod koji proverava sve moguće tipove koje O blik može da pokrije
prljav je i m orate da ga m enjate svaki put kada dodate novu vrstu O blika. U našem slu-
čaju, kažemo: ,,Ti si oblik, ja znam da možeš da se iscrtaš i obrišeš, to i uradi, i sam vodi
računa o detaljima".
Važno je da se naredbe u funkciji rađiN esto() izvršavaju na pravi način. Sam poziv
m etode iscrtajSe() za K rug prouzrokuje izvršavanje drugačijeg koda nego kada poziva-
mo m etodu iscrtajSe() za Kvadrat ili Liniju; ali kada poruku iscrtajSe() pošaljemo ne-
poznatom O bliku, dobija se ispravno ponašanje, 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 obično očekivali da on pozove verziju
m etoda obrisiSe() i iscrtajSe() za osnovnu klasu O blik, a ne za određeni Krug, Kvadrat
Poglavjje 1: Upoznavanje sa objektima 29

ili Liniju. Zbog polim orfizm a se sve ipak ispravno odvija. Prevodilac i sistem za izvršava-
nje vode računa o svim detaljima. Zasađ je dovoljno da znate da polim orfizam funkcio-
niše i kako da pišete program e koristeći taj pristup. Kada pošaljete poru k u objektu,
objekat će uraditi šta treba, čak i kada treba svoditi naviše.

Hijerarhija s jedinstvenim korenom


Jedno od pitanja u O O P-u koje je postalo veoma značajno od uvođenja 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 zajednički interfejs i, prem a to-
me, svi su istog osnovnog tipa. Postoji i druga m ogućnost (koju nudi C + + ), da svi objekti
ne budu istog osnovnog tipa. Po vertikalnoj kom patibilnosti ovo više odgovara m odelu
jezika C i može 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ć ugrađena u druge O O P jezike. Takođe, u svakoj novoj
biblioteci klasa koju nabavite, biće korišćen neki drugi nekom patibilni interfejs. Treba
uložiti napor (i verovatno višestruko nasleđivanje) 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 uložili u C - prilično je vredna. Ukoliko počinjete od nule, drugi jezici, poput
lave, često rnogu biti mnogo produktivniji.
Svi objekti u hijerarhiji s jedinstvenim korenom (kakvu obezbeđuje Java) zasigurno
imaju određenu zajedničku funkcionalnost - znate da možete da izvršite određene osnov-
ne operacije nad svakim objektom u svom sistemu. Hijerarhija s jedinstvenim korenom ,
uz stvaranje dinamičkih objekata, veoma pojednostavljuje prosleđivanja argum enata.
Hijerarhija sa jedinstvenim korenom olakšava realizaciju sakupljača smeća, što jeje d n a
od temeljnih prednosti Jave nad jezikom C++. Pošto je pri izvršavanju u svim objektim a
garantovano postojanje informacija o tipu, nikada nećete naići na objekat ćiji tip ne m o-
žete da utvrdite. To je naročito važno kod operacija na sistemskom nivou, kao što je ob-
rada izuzetaka, i obezbeđuje veću fleksibilnost pri program iranju.

Kontejneri
Po pravilu, ne možete znati koliko objekata će vam biti potrebno za rešavanje nekog
zadatka, niti koliko dugo oni treba da postoje u m emoriji. Ne znate ni kako da uskladištite
te objekte. Ako pre izvršavanja program a ne znate ni broj objekata ni njihov vek trajanja,
kako da odredite količinu memorijskog prostora za njihovo skladištenje?
Rešenje većine problem a u objektno orijentisanom program iranju ponekad deluje
neozbiljno: stvoriti još jedan tip objekta. Novi tip objekta koji rešava pom enuti problem
čuva reference na druge objekte. Naravno, to isto možete da uradite i pom oću nizova, koji
postoje u većini jezika. Ali taj novi objekat, obično pod im enom kontejner (naziva se i fco-
lekcija, ali pošto Javine biblioteke koriste taj term in u drugom smislu, ova knjiga će se
30 Misliti na Javi

držati im ena ,,kontejner“), širiće 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.
Srećom, dobar OOP jezik sadrži 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 prilično dobar skup kontejnera. I Java
ima m nogo kontejnera u svojoj standardnoj biblioteci. U nekim bibliotekama jedan ili
dva generička kontejnera dovoljni su za sve potrebe, dok u drugim bibliotekama (na pri-
m er u Javi) postoje različiti tipovi kontejnera za različite potrebe: više različitih klasa
spiskova (tipova Lista) za čuvanje sekvenci, tipova M apa (koje neki nazivaju asocijativni
tiizovi) zapridruživanje jednih objekata drugim a, tipova skupova (Set) za čuvanje po jed-
nog prim erka raznih tipova objekata i druge kom ponente m eđu kojima su redovi če-
kanja, stabla, stekovi itd.
Sa stanovišta program a, za vas je važan kontejner s kojim možete da radite i rešite pro-
blem. Ako kontejner jednog tipa zadovoljava vaše potrebe, nem a razloga da uvodite druge.
Izbor treba da vam bude ponuđen iz dva razloga. Prvo, kontejneri obezbeđuju različite ti-
pove interfejsa i načina rada. Stek ima interfejs i način rada različit od reda čekanja, koji se
razlikuje od skupa ili liste. Za rešenje vašeg problem a jedan od njih je obično bolji od osta-
lih. Drugo, različiti kontejneri obavljaju iste operacije s različitom efikasnošću. Na primer,
postoje dve osnovne vrste lista: ArrayList i LinkedList. I jedna i druga su jednostavne se-
kvence koje mogu imati identične interfejse i, spolja gledano, načine rada. No, postoji bit-
na razlika u trajanju i zahtevima odredenih operacija nad njima. Prim era radi, nasum ično
pristupanje elementima kontejnera ArrayList je operacija s konstantnim vremenom izvr-
šavanja; bez obzira na to koji element izaberemo, potrebno je isto vreme. Međutim, pom e-
ranje kroz listu LinkedList do nasum ično izabranog elementa je komplikovanije i treba
više vremena da se pristupi elementim a što su oni dalje od početka liste. S druge strane,
ako želite da ubacite element usred sekvence, to je mnogo jednostavnije i brže 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, možete krenuti od
kontejnera LinkedList a kada budete popravljali perform anse, prebacite se na kontejner
ArrayList. Zahvaljujući apstrakciji preko interfejsa List, prebacivanje iz jedne strukture 11
drugu imaće minim alan uticaj na vaš kod.

Parametrizovani (generički) tipovi


Pre Jave SE5, kontejneri su mogli da sadrže samo jedini univerzalni tip eleinenata koji
postoji u Javi: Object. H ijerarhija s jedinstvenim korenom znači da je sve tipa Object,
tako da kontejner koji može da čuva Object, može da čuva bilo šta." Ovo pojednostavljuje
ponovno korišćenje kontejnera.
Da biste koristili takav kontejner, đodajte u njega reference na objekte, a kasnije ih tražite
nazađ. M eđutim, pošto 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 reči u ovoj knjizi.
Poglavlje I : Upoznavanje sa objektima 31

objekat u kontejner, ona se svodi naviše 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 nešto š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 nasleđivanja do opštijeg tipa, već se spuštate niz hijerarhiju do od-
ređenijeg tipa. Ovaj naćin eksplicitne konverzije naziva se svođenje naniže (engl. dovvnca-
sting). Pri svođenju naviše, na primer, znate da je K rug tipa O blik, pa možete slobodno
da izvršite svođenje naviše; m eđutim , neki O bject nije obavezno K rug ili O blik, pa svo-
đenje naniže nije baš sigurno, osim ako znam o s čim im am o posla.
Ovo i nije previše opasno: ako izvršite pogrešno svođenje naniže, javlja se greška pri iz-
vršavanju - izuzetak (engl. exception), o kome uskoro govorimo. Kada iz kontejnera uzi-
m ate reference, m orate obezbediti neki naćin da zapam tite na šta se te reference odnose
kako biste mogli da izvršite pravilno svođenje naniže.
Svođenje naniže i provere pri izvršavanju zahtevaju dodatno vreme za izvršavanje pro-
gram a i dodatni napor za program era. Zar ne bi bilo logično da nekako napravim o kon-
tejner tako da on zna koje tipove čuva, čime se isključuje potreba za svođenjem naniže
kao i m oguće greške. Ovo se rešava pom oću parametrizovanih tipova, a to su klase koje
prevodilac autom atski može da prilagodi tako da rade sa određenim tipom . Na prim er,
param etrizovani kontejner prevodilac bi mogao da prilagodi tako da prihvata i vraća
sam o klasu Oblik.
ledna od velikih prom ena koje je donela Java SE5 jesu param etrizovani tipovi, koje u
Javi nazivamo generički tipovi. Prepoznaćete ih po uglastim zagradam a u n u tar kojih se
navode; recimo, ovako se može napraviti ArrayList koji sadrži 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 generićki tipovi dobro iskoristili, izmenjene su i mnoge standardne kom ponen-


te bibilioteke. Kao što ćete videti, generički tipovi su uticali na dobar deo koda u ovoj knjizi.

Pravljenje objekata i njihov životni vek


Jedan od najvažnijih činilaca jeste način na koji se objekti stvaraju i uništavaju. Da bi m o-
gao da postoji, svakom objektu su neophodni neki resursi, prvenstveno m em orija. Kada
objekat više nije potreban, on m ora da se počisti kako bi se resursi oslobodili i ućinili do-
stupnim za ponovno korišćenje. U jednostavnim program skim situacijama pitanje kako
očistiti objekat ne čini se previše komplikovanim: napravite objekat, koristite ga koliko
vam je potreban, a onda neka se uništi. M eđutim, moguce su i složenije situacije.
Pretpostavim o, na primer, da pišete program za upravljanje vazdušnim saobraćajem
na nekom aerodrom u. (Isti model bi mogao da se prim eni i na rukovanje sanducim a u
skladištu, ili na sistem i/.najmljivanja video kaseta, ili na smeštaj za kućne 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 uđe u zonu kontrole va-
zdušnog saobraćaja. L)a biste očistili sistem kada avion napusti zonu, počistite iz
m em orije odgovarajući objekat avion.
32 Misliti na Javi

Možda imate i neki drugi sistem za zapisivanje podataka o avionim a o kojima ne treba
neposredno voditi računa. M ožda je to evidencija o planovim a leta malih aviona koji na-
puštaju aerodrom . Znači, trebao bi vam drugi kontejner za male avione i kad god napra-
vite objekat aviona, ako je to mali avion, stavili biste ga takođe i u ovaj drugi kontejner.
Zatim bi neki pozadinski proces obavljao operacije nad objektim a iz tog kontejnera kada
računar ne radi ništa drugo.
Sada je problem nešto teži: kako uopšte možete znati kada da uništite objekte? Kada za-
vršite sa objektom, nekom drugom delu sistema m ožda je još uvek potreban. Isti problem
se može pojaviti i postati vrlo složen u m nogim drugim situacijam a i u program skim si-
stemim a (kao što je C++) u kojima m orate izričito da obrišete objekat kada s njim završite.
Gde se nalaze podaci iz objekta i kako se kontroliše trajanje objekta? C ++ zauzima stav
da je najvažnija efikasnost, pa program eru prepušta izbor. Da bi se postigla maksimalna
brzina izvršavanja, čuvanje i trajanje m ogu biti određeni 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 statičku oblast za čuvanje. Ta mesta im aju viso k p rio ritet i prostor
se u njim a brzo zauzima i oslobađa, pa je kontrola nad njim a veoma značajna u nekim si-
tuacijama. M eđutim , tim e žrtvujete fleksibilnost jer m orate da znate tačan broj, trajanje
i tip objekata prilikom pisanja program a. Ako pokušavate da rešavate opštiji problem , na
prim er projektovanje pom oću računara (CAD), upravljanje skladištem ili kontrolu va-
zdušnog saobraćaja, ova m etoda je previše restriktivna.
Drugi pristup je dinamičko stvaranje objekata u dinamičkoj oblasti memorije (engl.
heap). Pri ovom pristupu, sve dok ne počne izvršavanje, ne znate koliko vam objekata tre-
ba, koliko će trajati, niti kog su tipa. Sve se to određuie kada program već radi (ovakav na-
čin pravljenja objekata zove se dinam ički). Ako vam zatreba novi objekat, napravite ga u
dinamičkoj memoriji, u trenutku kada vam je zatrebao. Pošto se oblašću za čuvanje upra-
vlja dinamički, prilikom izvršavanja, zauzimanje memorijskog prostora traje znatno duže
od odvajanja prostora na steku. Odvajanje prostora na steku često se postiže samo asem
blerskom naredbom da se pokazivač steka pom eri naniže i da se vrati nazad. Vreme za koje
se odvoji prostor u dinamičkoj m em oriji, zavisi od dizajna m ehanizm a za skladištenje.
Dinamički pristup se zasniva na najčešće opravdanoj pretpostavci da su objekti slože-
ni, tako da dodatni režijski troškovi za nalaženje prostora i njegovo oslobađanje neće bit-
no uticati na stvaranje objekata. Veća fleksibilnost dinam ičkog pristupa je neophodna za
rešavanje opštijih program skih problem a.
Java isključivo koristi drugi pristup.' Svaki p u t kada želite da stvorite objekat, koristite
rezervisanu reč new da biste napravili dinam ički prim erak tog objekta.
Nameće se još i pitanje trajanja objekata. U jezicima koji dozvoljavaju da se objekti stva-
raju na steku, prevodilac određuje koliko dugo objekti traju i može automatski da ih uništi.
Međutim, kada objekat stvorite dinamički, prevodilac nema informacije o njegovom ži-
votnom veku. U jeziku kao što je C ++ m orate programski da odredite kada ćeteda uništite
objekat, što može dovesti do„curenja" m em orije ako se to ne uradi ispravno (to je čest pro-
blem u program im a pisanim na C + + -u). Java obezbeđuje tzv. sakupljač smeća (engl. gar-
bage collector) koji automatski otkriva koji se objekat više ne upotrebljava i uništava ga.

P ro sti tip o v i, o k o iitn a ćete k a sn ije s a z n a ti više, p re đ s ta v lja ju sp ec ija la n slučaj.


Poglavlje I : Upoznavanje sa objektima 33

Sakupljanje smeća je veoma korisno, jer sm anjuje broj stavki na koje m orate da mislite i
količinu koda koji m orate da napišete. Još je bitnije što sakupljanje smeća obezbeđuje
m nogo viši nivo zaštite od podm uklih problem a sa „curenjem" m em orije (koje je mnoge
projekte pisane na jeziku C++ oborOo na kolena).
U Javi, sakupljač smeća vodi računa o problem u oslobađanja m em orije'(iako u to ne
spadaju drugi aspekti čišćenja objekta). Sakupljač smeća ,,zna“ kada se objekat više ne ko-
risti i autom atski oslobađa 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 način za
pravljenje objekata - dinam ički - č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 prevaziđete.

Obrada izuzetaka: postupanje s greškama


Još od prvobitnih program skih jezika, obrada grešaka je bila jedna od najtežih oblasti.
Pošto je teško napraviti dobru šem u za obradu grešaka, u m nogim jezicima zanemarena
je ta oblast i to pitanje, a problem se prepušta projektantim a bibJioteka. Projektanti bi-
blioteka su iznašli polovične mere prim erene m nogim situacijama, ali koje se lako mogu
zaobići, obično ignorisanjem. O snovni problem sa svim šemam a za obradu grešaka 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 dešava kad žuri - te šeme lako može
ispustiti iz vida.
O brada izuzetaka ugrađuje obradu grešaka direktno u program ski jezik i ponekad čak
i u operativni sistem. Izuzetak je objekat ,,bačen“ (engl. thrown) s mesta greške, koji od-
govarajući upravljač izuzecima koji obrađuje ođređeni tip grešaka, može da ,,uhvati“
(engl. catch). Kao da je obrada izuzetaka drugi, paralelni put izvršavanja, kojim može da
se krene kada stvari pođu naopako. Zato što koristi poseban put za izvršavanje, obrada
izuzetaka ne m ora da se meša s vašim kodom koji se norm alno izvršava. Pošto ne morate
stalno da proveravate da li je biio grešaka, pisanje koda će najčešće biti jednostavnije. Sem
toga, bačen izuzetak se razlikuje od vrednosti greške koja je vraćena iz m etode i od indi-
katora stanja koji ukazuje na grešku, po tom e što oni mogu da se ignorišu. Izuzetak se ne
može ignorisati i garantuje se da će biti obrađen u nekom trenutku. Konačno, izuzeci
obezbeđuju da se pouzdano izvučete iz loše situacije. Umesto da samo izađete iz progra-
nia, često ste u prilici da sredite stvari i nastavite sa izvršavanjem, čime dobijate mnogo
robusnije program e.
Javina obrada izuzetaka se izdvaja od drugih program skih jezika jer je ugradena od
početka, pa ste prinuđeni da je koristite. To je jedini prihvatljivi način prijavljivanja greša-
ka. Ako ne napišete kod tako da pravilno obrađuje izuzetke, javiće se greška pri prevođe-
nju. Ova garantovana doslednost katkada znatno pojednostavljuje obradu grešaka.
Treba napom enuti da obrada izuzetaka nije sam o objektno orijentisana mogućnost,
iako su u objektno orijentisanim jezicima izuzeci obično predstavljeni objektima. O brada
izuzetaka je postojala i pre objektno orijentisanih jezika.
34 Misliti na Javi

Paralelni rad
Osnovni koncept u računarskom program iranju jeste obrada više od jednog zadatka isto-
vremeno. Mnogi programski poslovi zahtevaju da program može da zaustavi rad, poza-
bavi se nekim drugim problem om a zatim se vrati glavnom procesu. Problem u se
pristupalo na više načina. Na početku, program eri koji su poznavali računar 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 teško i neprenosivo, jer
je prebacivanje program a na novi tip računara bilo sporo i skupo.
Ponekad su za obradu zadataka koji se m oraju odm ah izvršiti prekidi rada neophodni,
ali postoji velika klasa poslova u kojoj problem pokušavam o da izdelimo na više delova
koji se izvršavaju zasebno i paralelno, da bi ceo program brže reagovao. Delovi koji se za-
sebno izvršavaju unutar program a nazivaju se niti (engl. thread), a ceo koncept paralelni
rad (engl. concurrency). Tipičan p rim er paralelnog rada je korisničko okruženje. Zbog
podele program a na niti, korisnik m ože da pritisne dugm e i da dobije brz odziv, umesto
da bude prinuđen da čeka dok program ne završi tren u tn i zadatak.
N itim a se obično raspodeljuje vreme jednog (i jedinog) procesora. M eđutim , ako ope-
rativni sistem podržava višeprocesorski rad, svaka nit može biti dodeljena različitim pro-
cesorima, i tada one zaista m ogu da rade paralelno. Jedna od korisnih osobina paralelnog
rada na nivou jezika jeste sledeća: program er ne m ora da brine postoji li jedan ili više pro-
cesora. Program je logički podeljen na niti i ako računar ima više od jednog procesora,
program će se brže izvršavati, bez ikakvih posebnih prilagođavanja.
Nakon svega ovoga, paralelni rad zvuči prilično jednostavno. Postoji ipak jedna zač-
koljica: deljeni resursi. Ako se sim ultano izvršava više niti koje očekuju da pristupe istom
resursu, pojaviće se problem. Na prim er, dva procesa ne mogu istovrem eno da šalju po-
datke istom štampaču. Da bi se rešio problem , resursi koji mogu biti deljeni, kao što je
štam pač, m oraju biti zaključani dok se koriste. Znači, nit zaključa resurs, završi zadatak,
a zatim otključa resurs, posle čega neko drugi može da ga koristi.
Paralelni rad je ugrađen u Javu, a Java SE5 ga d odatno podržava svojom bililiotekom.

Java i Internet
Ako je Java zapravo samo još jedan računarski program ski jezik, m ožete se zapitati zašto
je toliko važna i zašto se predstavlja kao revolucionarni korak u računarskom programi
ranju. Odgovor nije odmah očigledan, ukoliko se posm atra iz tradicionalne program er-
ske perspektive. Iako je Java veoma korisna za rešavanje uobičajenih zasebnih
program skih problem a, još je važnije to što pom oću nje možete rešiti i program ske pro-
bleme koji se tiču Weba.

Šta je Web?
Isprva Web može da deluje pom alo tajanstveno, uz celu priču o „krstarenju“, ,,prisustvu“
i „Iičnim 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 računarske obrade koji je
pun zbunjujućih tema.
Poglavlje I : Upoznavanje sa objektima 35

Klijent/server obrada
O snovna ideja sistema klijent/server jeste postojanje centralnog skladišta inform acija -
neke vrste podataka, obično u bazi podataka - koje hoćete da šaljete, po zahtevu, grupi
ljudi ili računara. U konceptu klijent/server ključno je to što je skladište inform acija cen-
tralizovano tako da informacije m ogu da se m enjaju i da se te prom ene prenose svim ko-
risnicima informacija. Skladište inform acija, softver koji šalje informacije i računar
(računari) gde se softver i inform acije nalaze, nazivaju se server. Softver koji se nalazi na
korisničkom računaru, sarađuje sa serverom, preuzim a informacije, obrađuje ih i prika-
zuje na udaljenom računaru, naziva se klijent.
Osnovni koncept klijent/server obrade, znači, nije previše složen. Problemi se javljaju
jer im ate jedan server koji pokušava da opsluži više klijenata u isto vreme. Uglavnom se ko-
risti sistem za upravljanje bazam a podataka, tako da program er ,,uravnotežava“ raspored
podataka u tabelama da bi postigao optim alno korišćenje. Sistemi često dozvoljavaju kli-
jentim a i da dodaju informacije na server. To znači 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, očisti od grešaka i instalira na
klijentskim računarim a, što je u stvarnosti znatno komplikovanije i skuplje nego što mi-
slite. Posebno je problem atično podržati više tipova računara i operativnih sistema. Ko-
načno, tu je i veoma bitno pitanje perform ansi: stotine klijenata mogu da postavljaju
zahteve vašem serveru u bilo kom trenutku, pa je i najmanje zakašnjenje problematično.
Da bi se kašnjenje svelo na najm anju m eru, program eri naporno rade da rasterete proce-
sne zadatke, prebacujući deo obrade na klijentski računar, a ponekad i na druge računare
na serverskoj strani, koristeći takozvane posrednike (engl. middleware). (Posrednici se ta-
kođe koriste da bi se olakšalo održavanje.)
Realizacija tako jednostavnog postupka - slanja informacija - toliko je slojevita i slo-
žena da ceo problem đeluje beznadežno zagonetno. I pored toga je veoma bitna: klijent/
server obrada nosi otprilike polovinu svih program skih aktivnosti. O na je zadužena za
sve, počev od uzimanja narudžbina, transakcija s kreditnim karticama, pa do slanja ra-
znih vrsta podataka - berzanskih, naučnih, državnih; šta god vam padne na pam et. U
prošlosti sm o se suočavali sa individualnim rešenjima individualnih problema; svaki put
sm o smišljali novo rešenje. Bilo je teško osmisliti ih, teško su se upotrebljavala i korisnik
ie m orao da uči nov interfejs za svako od tih rešenja. Ceo klijent/server problem trebalo
bi sveobuhvatno da se reši.

Web kao džinovski server


Web je u suštini jedan džinovski sistem klijent/server, i nešto više od toga, jer svi serveri i
svi klijenti postoje zajedno i u isto vreme 11 samo jednoj mreži. 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 Mrežom tražeći odgovarajući server).
U početku je to bio jeđnostavan jednosmerni proces. Vi ste ispostavljali zahtev serveru
i on vam je prosleđivao datoteku koju je softver za čitanje (klijent) prevodio i formatirao na
vašem lokalnom računaru. Ali, ubrzo su ljudi poželeli da rade više od čistog prosleđivanja
36 Misliti na Javi

stranica sa servera. Želeii su punu klijent/server kompatibilnost kako bi klijent bio u mo-
gućnosti da vraća podatke mogućnost na server, na primer, da pretražuje baze na serveru,
dodaje nove informacije na server, ili da ostavi narudžbinu (što je sve zahtevalo posebne
bezbednosne mere). Bili smo svedoci tih prom ena u razvoju Weba.
Čitač Weba je označio veliki korak napred - uveo je m ogućnost da se delići inform acija
neizm enjeni prikazuju na bilo kom tipu računara. Ti čitači su ipak bili prilično prim itivni
i ubrzo su se zaglibili u zahtevima koji su im ispostavljani. Oni nisu bili previše interak-
tivni; zagušivali su server i sam Internet jer ste, svaki p u t kada je trebalo uraditi nešto što
zahteva program iranje, m orali da vraćate informacije serveru na obradu. Moglo je da
prođe više sekundi ili m inuta dok ne otkrijete da ste nešto pogrešno otkucali. Pošto je či-
tač bio predviđen samo za pregled, nije m ogao da izvrši ni najjednostavnije zadatke ob-
rade. (S druge strane, bio je po tp un o bezbedan, jer na vašem računaru nije mogao da
izvrši nijedan program , dakle ni da m u potencijalno donese grešku ili virus.)
Prim enjeno je više pristupa rešenju ovog problema. Za početak, poboljšani su grafički
standardi da bi omogućili bolju animaciju i video prikaz u čitačima. Drugi deo problema
mogao je biti rešen samo ugrađivanjem u čitač mogućnosti za pokretanje program a na kli-
jentskoj strani. To se naziva programiranje s klijentskestrane (engl. client-sideprogramming).

Programiranje s klijentske strane


Početni dizajn Weba (server-čitač) om ogućavao je interaktivne sadržaje, ali je celokupnu
interaktivnost obezbeđivao server. Server je pravio statičke stranice za klijentski čitač koji
ih je tum ačio i prikazivao. Elem entarni jezik za označavanje hiperteksta (HTML) ima jed-
nostavne m ehanizm e za prikupljanje podataka: polja za tekst, polja za potvrdu, radio-
-dugm ad, liste i padajuće liste, kao i dugm e koje može biti program irano samo da obriše
podatke u obrascu ili da pošalje (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. Najčešća akcija je da se pokrene program koji se nalazi na serveru, u direk-
torijum u koji se obično zove ,,cgi-bin“. (Ako budete gledali adresno polje na vrhu svog
čitača kada pritisnete neko dugm e na Web stranici, ponekad ćete videti ,,cgi-bin“ negde
u nu tar sveg onog zamešateljstva.) 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 može biti instaliran na bilo koji server, bez o b /ira na procesor ili operativni sistem. Da-
nas se sve više koristi Python ( www.Python.org), zato što je jači i jednostavniji.
M noge današnje m oćne VVeb stranice izgrađene su isključivo na CGI program im a,
s kojima možete da uradite gotovo sve. Ipak, održavanje Web stranica izgrađenih na CGI
program im a može brzo da postane suviše složeno, a postoji i problem s vrem enom od/.i-
va. Odziv CGI program a zavisi od toga koliko podataka se šalje, kao i od opterećenja 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 oguće dosledno ostvariti bilo koju vrstu dinamičkog
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 grešku, form atira HTML stranicu obaveštavajući vas o grešci, a za-
tim vam vrati tu stranicu. O nda vi m orate da se vratite na p reth o d n u stranicu i pokušate
ponovo. Ovo ne samo da je sporo već je i zam orno.
Rešenje je program iranje s klijentske strane. Većina stonih računara na kojima rade či-
tači Weba sposobni su da urade ogrom an posao, a s prvobitnim statičkim HTM L pristu-
p om sam o su stajali i besposleno čekali da server isporuči sledeću stranicu. Program iranje
s klijentske strane znači da čitač Weba radi sav posao koji može da obavi, korisnik m nogo
brže dobija rezultat a osećaj zajedništva p ri radu s Web stranicom je potpuniji.
Rasprave o program iranju s klijentske strane malo se razlikuju od rasprava o progra-
m iranju uopšte. Param etri su gotovo isti, ali je platform a drugačija: čitač Weba je sličan
ograničenom operativnom sistemu. Na kraju, opet m orate da program irate, i zato pro-
gram iranje s klijentske strane povlači vrtoglav niz problem a i rešenja. U daljem tekstu da-
jem o pregled pitanja i pristupa pri program iranju klijentske strane.

Dodaci
Razvoj dodataka (engl. plug-ins) predstavlja jedan od najdužih koraka napred u progra-
m iranju s klijentske strane. Program er dodaje novu funkcionalnost čitaču tako što kori-
snik u potrebnom trenutku preuzme deo koda koji se ugradi na odgovarajuće m esto u
čitaču. Taj kod govori čitaču: ,,Od sada možeš da obavljaš i ovu novu aktivnost“. (D odatak
treba da preuzm ete sam o jednom .) Preko dodataka su čitačima pridodata brza i m oćna
proširenja, ali pisanje dodataka nije jednostavan zadatak i nije nešto što biste želeli da ra-
dite u okviru izgradnje odredene stranice. Vrednost đodataka za program iranje s klijent-
ske strane jeste to što oni omogućavaju stručnjaku da pravi nova proširenja i da ih dodaje
čitaču bez odobrenja proizvođača čitača. Stoga, dodaci predstavljaju „zadnja vrata“ koja
om ogućavaju 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 čitače. Pomoću skript-jezika ugradujete
izvorni kod program a za klijentsku stranu direktno u HTML stranicu, a dodatak koji tu-
mači taj jezik autom atski se aktivira pri prikazivanju HTML stranice. Skript-jezici se
obično prilično lako shvataju i pošto se pišu u delu HTML stranice, učitavaju se veoma
brzo, jednim pristupom serveru na kome se nalazi ta stranica. Nedostatak je to što je vaš
kod izložen i svako može da ga vidi (i ukrade). U principu, ipak ne pravite neke sofistici-
rane stvari pom oću skript-jezika, pa ovaj nedostatak i nije neki problem .
Postoji jedan skript-jezik koji većina čitača Weba podržava i bez ikakvog dodatka - to
je JavaSoript (koji ima tek prolaznu sličnost s Javom, i m oraćete ga učiti zasebno). Tako je
nazvan sam o da bi prigrabio deo Javinog marketinškog uzleta. Nažalost, većina čitača
Weba je svoju podršku za JavaScript realizovala na svoj jedinstveni način, drugačije od
ostalih čitača, pa čak i od ostalih svojih verzija. Nešto je popravila standardizacija Java-
Scripta u obliku ECMAScipta, ali je raznim čitačima trebalo m nogo vrem ena da dođu do
38 Misliti na Javi

tog nivoa (tom e je doprineo i M icrosoft, gurajući svoj VBScript koji pomalo liči na Java-
Script). Da bi program m ogao da se izvršava na svim čitačim a,po pravilu ga m orate pisati
koristeći najm anji zajednički imenilac svih postojećih verzija JavaScripta. Programska
obrada grešaka, kao i otkrivanje i otldanjanje grešaka u toku pisanja program a, mogu se
opisati sam o kao mučenje. Dokaz tili poteškoća je činjenica da je na JavaScriptu tek ne-
davno napisan zaista složen program (to je Googleov GMail), što je zahtevalo veliki trud
i stručnost.
Ovim se naglašava da su skript-jezici koji se koriste u nutar čitača Weba predviđeni da
reše određene vrste problem a, prvenstveno da se naprave bogatija i interaktivnija grafička
korisnička okruženja. Skript-jezik m ože da reši i do 80 procenata problem a koje srećemo
pri program iranju s klijentske strane. Vaši problem i verovatno spadaju u tih 80 procena-
ta, a kako skript-jezici om ogućuju lakši i brži rad, trebalo bi da razmislite o njim a pre
nego što se upustite u m nogo zapetljanija rešenja, kao što je program iranje na Javi.

Java
Ako skript-jezici mogu da razreše 80 procenata problem a pri klijentskom program iranju,
šta je s preostalih 20 procenata „zaista teških stvari“? Java je popularno rešenje za tih 20%.
Pre svega, to je m oćan program ski jezik, siguran, m eđuplatform ski i internacionalan.
Java se neprekidno širi: dodaju se nove m ogućnosti jezika i biblioteke koje mogu elegant-
no da reše problem e teške i u tradicionalnim program skim jezicima, kao što su istovre-
meni rad, pristup bazama, m režno program iranje i distribuirana obrada. Java dopušta
program iranje s klijentske strane preko apleta i pom oću lava Web Starta.
Aplet je mali program koji može da se izvršava samo unutar ćitača 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 izvršava program . Deo njegove pogodnosti je to što
omogućava 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 složene ponovne instalacije. Zbog načina na koji je
Java projektovana, program er treba da napravi samo jedan jedini program, a taj program
autom atski radi na svim računarim a koji imaju čitače s podrškom za Javu. (Ovo, bez ika-
kve brige, obuhvata ogrom nu većinu računara.) Pošto je Java potpun programski jezik,
možete 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 pogrešili pri upisivanju datum a ili nekog drugog param etra; vaš
računar može brzo da iscrtava grafike na osnovu podataka, um esto da čeka da ih server is-
crta i vrati sliku. Pored trenutnog poboljšanja brzine i odziva, sm anjuju se ukupni mrežni
saobraćaj i opterećenje servera i sprečava usporavanje celog Interneta.

Druge mogućnosti
Pošteno rečeno, Java apleti nisu opravdali velika početna očekivanja. Kada se Java tek po-
javila, najviše se govorilo upravo o apletim a, jer je trebalo da oni najzad omoguće ozbiljno
program iranje na klijentskoj strani, povećaju brzinu odziva i smanje protok podataka
potreban Internet aplikacijama. Predviđale su se ogrom ne mogućnosti.
Poglavlje 1: Upoznavanje sa objektima 39

Na Webu se zaista m ogu naći i veoma pam etni apleti, ali do sveopšteg prelaska na aple-
te nije došlo. Verovatno je najveći problem bila veličina Javinog izvršnog okruženja (Java
Runtim e Environm ent, JRE) od 10 MB, koje je trebalo preuzeti s Weba i instalirati, što je
uplašilo prosečnog korisnika. Sudbinu im je m ožda zapečatila činjenica da je Microsoft
odlučio da JRE ne isporučuje kao deo svog Internet Explorera. U svakom slučaju, Java
apleti se nisu proširili posvuda.
Bez obzira na to, u nekim situacijama Java aplete i Java Web Start aplikacije još uvek
vredi imati. Ukoliko upravljate računarim a korisnika, recimo unutar preduzeća, bilo bi
p am etno da distribuciju i ažuriranje klijentskih aplikacija obavljate pom oću ovih tehno-
logija, jer ćete tim e uštedeti znatnu količinu vrem ena, tru d a i novca, naročito ako m orate
često da ažurirate njihov softver.
U poglavlju Grafička korisnička okruženja upoznaćem o Flex, novu Adobeovu tehno-
logiju koja obećava - u njom e se prave Flash apleti. Pošto više od 98 procenata svih čitača
Weba im a Flash Player (na W indowsu, Linuxu i M acu), m ožem o sm atrati da je on usvo-
jen standard. Flash Player se instalira i ažurira lako i brzo. Jezik ActionScript je napravljen
na osnovu ECMAScripta i stoga je prilično poznat, ali Flex omogućava program iranje bez
brige o specifičnostima raznih čitača - zato je daleko privlačniji od JavaScripta. Pri pro-
gram iranju na klijentskoj strani, ovu m ogućnost vredi razm otriti.

.NETi C#
Neko vrem e je glavni konkurent Java apleta bio Microsoftov ActiveX, iako samo na W in-
dovvs računarim 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 približno isto što i Javina vir-
tuelna mašina (Java Virtual Machine, JVM) plus Java biblioteke. JVM je softverska plat-
form a na kojoj se izvršavaju java program i, a C# ima velike sličnosti s Javom. Bez sum nje,
radi se o dosad najboljem Microsoftovom proizvodu na području program skih jezika i
program skih okruženja. Naravno, Microsoft je imao veliku prednost jer je mogao da uči
na tuđim greškama, ali je bogami naučio. Prvi put od svog nastanka, Java je dobila pravu
konkurenciju. Zato su projektanti Jave u Sunu dobro pogledali C#, dobro razmislili o
tom e zašto bi program eri hteli da pređu na C#, i odgovorili: načinili su fundam entalna
poboljšanja Jave - Javu SE5.
T renutno je najveća siabost .NET-a važno pitanje da li će Microsoft dozvoliti njegovo
potpm io prenošenje na đruge platforme. Microsoft tvrdi da se to može napraviti bez pro-
blema, i već postoji delimična realizacija .NET-a koja radi na Linuxu (projekat Mono,
www.gomono.com), ali dok se ne napravi potpuna realizacija iz koje M icrosoft nije ništa
izbacio, .NET je rizično sm atrati rešenjem za sve platforme.

Internet i intranet
Wcl> je najopštije rešenje problem a klijent/server, pa je logično da istu tehnologiju
upotrebite i da biste rešili podgrupu tog problem a, posebno klasični problem klijent/ser-
ver um itar preduzeća. Pri tradicionalnom pristupu klijent/server, postoji problem zbog
raznih tipova računara kao i problem zbog teškog instaliranja novog klijentskog softvera.
Oba problem a dobro su rešena pom oću čitača Weba i program iranja s klijentske strane.
40 Misliti na Javi

Kada se tehnologija Weba koristi za inform acionu m režu ograničenu na određeno pre-
duzeće, to nazivamo intranet. Intranet obezbeđuje m nogo veću sigurnost nego Internet
jer pristup serverima u preduzeću možete fizički da kontrolišete. Izgleda da korisnici,
kadajednom shvate osnovni princip rada čitača, m nogo lakše izlaze na kraj s različitim iz-
gledima stranica i apleta, pa brže uče nove sisteme.
Problem s bezbednošću 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 izvršava 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 nešto nezavisno od platform e i bezbeđno, kao što su
skript-jezik ili Java.
Ako radite na intranetu, pred vas se postavljaju drugačija ograničenja. Nije neuobiča-
jeno da svi računari budu platform e Intel/W indows. Na intranetu ste sami odgovorni za
kvalitet svog program a i možete da otklanjate greške kako se koja otkriva. Pored toga,
možda već imate dosta nasleđenog koda koji ste koristili za tradicionalniji klijent/server
pristup, pri čemu m orate fizički da instalirate ldijentske program e svaki p u t kada radite
na poboljšanju. Vreme protraćeno na instaliranje poboljšanja pruža razlog više da pređe-
m o na čitače gde su poboljšanja nevidljiva i autom atska. (Ovaj problem rešava i Java Web
Start.) Ako imate posla s takvim intranetom , najrazboritiji pristup je da krenete najkra-
ćim putem koji vam omogućava da koristite postojeću bazu koda, um esto da ponovo pi-
šete program e na novom jeziku.
Kada se suočite sa ovim zbunjujućim nizom rešenia za program iranje s klijentske stra-
ne, najbolji plan napada je analiza isplativosti. Razm otrite ograničenja koja vaš problem
nameće i najkraći put do rešenja. Pošto je program iranje s Jdijentske strane ipak progra-
miranje, uvek je dobro odabrati pristup koji omogućava najbrži razvoj u određenoj situa-
ciji. Ovo je hrabar stav koji vas priprem a za neizbežne 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
mišljenju mnogih Java imala najveće uspehe. Šta se dešava kada pošaljete zahtev serveru?
Najčešće zahtevi glase: „Pošalji mi ovu datoteku". Vaš čitač zatim tum ači tu datoteku na
odgovarajući način: kao HTML stranicu, sliku, Java aplet, skript itd.
U složenije zahteve serveru obično spadaju i transakcije s bazama podataka. O bično 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 sadrži kod na Javi
ili skript-jeziku, mogu se slati neobrađeni podaci, a potom form atirati na strani klijenta,
što bi bilo brže i manje bi opterećivalo server.) M ožda biste poželeli da registrujete svoje
ime u bazi podataka kada pristupate nekoj grupi ili ostavljate narudžbinu, što će izazvati
prom ene u toj bazi. Pisanje program a koji obrađuju 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 eđu njim a su i Web serveri zasno-
vani na Javi, koji omogućavaju 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, najviše
zato što se tim e isključuju problem i pri radu sa čitačima različitih m ogučnosti. Progra-
miranje sa scrverske strane razm otreno je u knjizi Thitikingin Enterprise Java, koju može-
te nači na adresi www.MindView.net.
Uprkos tom e što se o Javi priča sam o u vezi sa Internetom , ona je programski jezik opšte
nam ene pom oću kogae možete rešiti bilo koji tip problem a, kao i pom oću drugih jezika.
Tu Javina snaga nije samo u prenosivosti, već i u lakoći program iranja, robusnosti, velikoj
standardnoj biblioteci i brojnim bibliotekama drugih proizvođača koje se stalno razvijaju.

Sažetak
Znate kako izgleda proceduralni program : definiciie podataka i pozivi funkcija. Da biste
pronašli svrhu takvog program a, m orate malo da se pom učite 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 načini izražavanja usm ereni više ka računaru nego ka
problem u koji rešavamo.
Pošto OOP dodaje m noge nove ideje onim a koje nalazite u proceduralnim jezicima,
možda mislite da će Java program biti m nogo komplikovaniji nego ekvivalentni C pro-
gram. Bićete prijatno iznenađeni: dobro napisan Java program je uglavnom daleko jed-
nostavniji i m nogo razumljiviji od ekvivalentnog C program a. Imaćete definicije objekata
koji predstavljaju ideje u vašem prostoru problem a (um esto pitanja računarskog 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 bično ima i znatno m anje koda, jer ćete m noge svoje pro-
bleme rešiti ponovnim korišćenjem postojećeg koda iz biblioteka.
O O P i Java ne m oraju da budu za svakoga. Važno 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 (uključujući onaj koji tren u tno koristite). Ako znate da će vaše
potrebe biti usko specijalizovane u bliskoj budućnosti i ako imate specifična ograničenja
koja Java m ožda ne može da zadovolji, onda ispitajte ostale raspoložive m ogućnosti. Kon-
kretno, preporučujem da razm otrite Python; posetite www.Python.org. Ako ipak izabere-
te Javu, barem ćete znati koje ste m ogućnosti imali i zašto ste baš nju izabrali.
Sve je objekat
,,Da govorimo drugačiji jezik, percipirali bismo tiešto drugačiji 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 može se koristiti više program skih stilova;
C++ je hibridan da bi obezbedio povratnu kom patibilnost s jezikom C. Pošto je C++
nadskup jezika C, on sadrži i m noge nepoželjne nasleđene osobine koje previše 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 uopšte upustite u program iranje, m orate početi da raz-
mišljate objektno-orijentisano (osim ako već ne razmišljate tako). Ovaj početni napor će
vam se isplatiti jer ćete biti sposobni da program irate na jeziku koji se uči i koristi jedno-
stavnije od mnogih drugih O O P jezika. U ovom poglavlju razm atraćem o osnovne delove
Java program a i naučićemo da je u Javi (gotovo) sve objekat.

Rad sa objektima preko referenci


Svaki program ski jezik ima svoje načine za rad sa elem entim a u m emoriji. Program er če-
sto ne sme da gubi iz vida način 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 upravljačem (re-
ferenca). Sve dok držite tu referencu, imate vezu s televizorom, ali kada neko kaže da pro-
menite kanal ili da utišate zvuk, vi radite s referencom koja menja objekat. Ako želite da se
šetate po sobi, a da i dalje podešavate televizor, ponećete 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 ažu : „ O čito , to jc pokazivač", ali tak av iskaz već sadrži
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 češće 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 neće p rev iše s m e ta ti. M e d u tim , neki se n e slažu č 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 rcći 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 rše 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 Iažem , ali ja ć u reći 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č može da postoji sam za sebe, bez televizora. O dnosno, to što imate
referencu, ne znači da obavezno im ate i objekat povezan s njom . Ako želite da čuvate reč
ili rečenicu, treba da napravite referencu na objekat ldase String:

String s;

Ali, ovim ste napravili satno referencu, ne i sam objekat. Ako pokušate u ovom trenut-
ku da pošaljete poruku preko s, dobičete grešku pošto 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 ože inicijalizovati tekstom pod
navodnicim a. Obično, za objekte m orate da koristite opštiji tip inicijalizacije.

Morate da napravite sve objekte


Kada napravite referencu, želite i da je povežete s novim objektom . Po pravilu, to radite
pom oču rezervisane reči new kojom se kaže: „Napravi m i ovakav nov objekat“. Znači, u
gornjem prim eru možete napisati:

String s = n ew S t r i n g ("asdf");

Ovo ne samo da znači: „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 početni znakovni niz.
Pored tipa String Java ima obilje drugih unapred definisanih tipova. M nogo je važnije
da možete napraviti svoje tipove. U stvari, to je osnovna aktivnost u program iranju na
Javi i o tome čete učiti u ovoj knjizi.

Gde se nalazi skladište


Korisno je predstaviti neke aspekte izvršavanja program a, a posebno kako je organizova-
na memorija. Postoji pet različitih mesta za skladištenje podataka:
1. Registri: Tu se najbrže skladište pođaci jer su registri ođvojeni od ostalih skladišta:
nalaze se unutar procesora. M eđutim, broj registara je veoma ograničen, pa se regi-
stri automatski dodeljuju u skladu s potrebam a. Vi nem ate direktnu lcontrolu, niti
u vašem program u vidite ijedan dokaz da registri uopšte postoje. (S druge strane, C
i C ++ omogućavaju da prevodiocu sugerišete dodelu registara.)
2. Stek: Naiazi se u radnoj m emoriji, ali ga procesor direktno kontroliše preko poka-
zivača stcka (engl. stack pointer). Pokazivač steka se pom era naniže da bi zauzeo
novu m emoriju, a pom era se naviše da bi tu m em oriju oslobodio. Ovo je veoma
brz i efikasan način za dodeljivanje skladišta, od koga su brži samo registri. )ava si-
stem mora da zna, dok pravi program , tačan životni vek svih stavki koji se čuvaju
na steku. Ovo ograničenje omeđava fleksibilnost vašeg program a, pa dok se neka
Javina skladišta nalaze na steku - posebno reference na objekte - sami objekti se ne
ćuvaju na steku.
44 Misliti na Javi

3. Dinamička memorija: To je oblast m em orije opšte nam ene (takođe u radnoj me-
m oriji) gde obitavaju svi objekti. D obra strana dinamičke m em orije jeste to što, za
razliku od steka, prevodilac ne m ora da zna koliko dugo dodeljeno skladište m ora
da ostane u dinamičkoj m em oriji. Stoga im am o veliku fleksibilnost pri korišćenju
dinam ičke memorije. Kad god vam zatreba neki objekat, napišite kod za njegovo
pravljenje koristeći new, i skladište će se napraviti u dinamičkoj memoriji. Narav-
no, m orate platiti ovu fleksibilnost: skladište u dinamičkoj m em oriji duže se pravi
nego skladište na steku (kad biste uopšte mogli da napravite objekte na steku u Javi,
kao što možete u C + + -u).
4. Konstantno skladište: K onstantne vrednosti često se um eću direktno u kod pro-
gram a što je bezbedno jer se one nikad ne m ogu prom eniti. Ponekad se konstante
izdvojeno grupišu 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 računarim a ugrađenim u
druge uređaje.2
5. Skladišta van radne memorije: Ako su podaci p otpuno izvan program a, oni mogu
da postoje i kada se program ne izvršava, 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, obično da bi se poslali na drugu m ašinu, i trajni
objekti (engl. persistent objects), što su objekti stavljeni na disk da bi sačuvali svoje
stanje čak i kada se program završi. U ovim skladištima teško je pretvoriti objekte u
nešto što može da postoji na drugom m edijum u, a što može da postane regularni
m em orijski objekat kada je potrebno. Java obezbeđuje podršku za laku trajnost
(engl. lightvveight persistencc), a m ehanizm i kao što su JDBC i Hibernate obez-
beđuju sofisticiraniju podršku za skladištenje i preuzim anje inform acija o objekti-
ma u bazama podataka.

Specijalan slučaj: prosti tipovi


Jedna grupa tipova koji se često koriste u program iranju ima poseban tretm an; možete ih
sm atrati osnovnim tipovima. Taj poseban tretm an je neophodan iz sledećeg razloga: ako
napravite objekat operatorom new - posebno malu, jednostavnu promenljivu - to neće
biti baš efikasno jer new postavlja objekte u dinam ičku mem oriju. Za ove tipove Java se
vraća na pristup koji imaju C i C++: um esto da napravi promenljivu korišćenjem 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 veličinu svakog prostog tipa. Ove veličine se ne menjaju od jed-
nog do drugog računara, kao što je slučaj u većini jezika. Nepromenljivost veličina je je-
dan od razloga zbog kog su program i na Javi prenosiviji nego oni na većini drugih jezika.

P r im e r za to je sk lad ište 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 ište 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 Veličina u bitovima M inimum Maksimum Om otački 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 erički tipovi su označeni (sadrže i pozitivne i negativne vrednosti), stoga ne
tražite neoznačene tipove.
Veličina tipa boolean nije izričito definisana; zadato je samo da može da im a vrednosti
true ili false.
Om otačkc klase za proste tipove podataka omogućavaju da u dinamičkoj m em oriji
napravite objekat koji predstavlja određen 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 ) ;

Možete takođe 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 biće prikazani dalje u


knjizi.

Brojevi visoke preciznosti


Java sadrži dve klase za obavljanje aritm etičkih operacija visoke preciznosti: B iglnteger i
BigDecimal. Iako one otprilike potpadaju pod istu kategoriju kao i om otačke 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 možete uraditi sve što i
sa tipovim a in t ili float, samo m orate koristiti m etode umesto operatora. A pošto je zađa-
tak komplikovaniji, operacije će liiti sporije. Brzinu zamenjujete tačnošću.
46 Misliti na Javi

Biglnteger podržava celobrojne vrednosti proizvoljne veličine. To znači da možete


tačno predstaviti celobrojne vrednosti bilo koje veličine, a da pri operacijam a ne izgubite
informacije.
BigDecimal služi za brojeve s pokretnim zarezom proizvoljne veličine; njih možete, na
primer, koristiti za precizna novčana izračunavanja.
U dokum entaciji JDK potražite pojedinosti o konstruktorim a i m etodam a ove dve
klase.

Nizovi u Javi
Praktično svi program ski jezici podržavaju neku vrstu nizova. Korišćenje nizova u C -u i
C + + - U rizično 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), doći će do nepredvidljivih situacija.
Jedan od osnovnih ciljeva Jave je sigurnost; u njoj se uopšte ne pojavljuju m nogi pro-
blemi koji muče program ere u C-u i C + + -u. U Javi je niz garantovano inicijalizovan i ne
može m u se pristupiti van njegovog opsega. Opseg se proverava po cenu male količine
dodatnog utroška m em orije u svakom nizu, kao i uz proveru indeksa pri izvršavanju, ali
se pretpostavlja da su sigurnost i povećana produktivnost vredni toga (a Java katkada
može 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 naiđe na null, ona prepoznaje da dotična referenca ne pokazuje na obje-
kat. Pre nego što je upotrebite, svakoj referenci m orate da dodclite objekat, i ako pokušate
da koristite referencu koja još uvek ima vrednost null, problem će biti prijavljen pri izvr-
šavanju. Na taj način su tipične greške s nizovima predupređene 11 Javi.
Možete 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 objašnjeni u narednim poglavljima.

Nikada ne morate da uništite objekat


Ostvarenje koncepta životnog veka prom enljive u većini program skih jezika vrlo je
naporno. Koliko dugo traje promenljiva? Ako treba da je uništite, kada bi to trebalo da
uradite? Konfuzija zbog životnog veka prom enljive može da đovede do mnogih grešaka,
a u ovom odeljku pokazujem o da Java znatno pojednostavljuje ovo pitanje, obavljajući
umesto vas sve čišćenje.

Oblast važenja
Većina proceduralnih jezika počiva na oblasti važcnjn (engl. scope). O na određuje vidlji-
vost i životni vek imena definisanih u n u tar te oblasti. U C-u, C + + -u i Javi oblast važenja
je određena vitičastim 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 važenja"
}
Promenljiva definisana unutar oblasti važenja dostupna je samo do kraja te oblasti.
Sav tekst nakon ’/ / ’ pa do kraja reda predstavlja komentar.
Uvlačenje pom aže da se program pisan u Javi lakše čita. Pošto je Java jezik slobodne
forme, dodatni razmaci, tabulatori i novi redovi ne utiču na rezultujući program.
U Javi ne tnožete da uradite sledeće, 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 mogućnost C-a i C++-
a da sakrije promenljive u većoj oblasti nije dozvoljena, je rsu projektanti Jave mislili da to
vodi do program a koji zbunjuju.

Oblast važenja objekata


O bjekti u (avi nem aju isti životni vek kao prosti tipovi. Kada napravite objekat u Javi ko-
risteći operator new, on postoji i nakon kraja oblasti važenja 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 važenja

referenca s će nestati na kraju oblasti važenja. M eđutim, objekat klase String na koji je s
ukazivala još uvek zauzima mem oriju. U ovom deliću koda neina načina da pristupite
objektu nakon oblasti važenja, jer je jedina referenca na njega nedostupna izvan oblasti
važenja. Dalje u knjizi videćete kako reference na objekte mogu da se prosleđuju i um no-
žavaju celim tokom programa.
Pošto se objekti napravljeni pom oću operatora new zadržavaju dokle god su vam po-
trebni, ispostavlja se da mnoštvo 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 uništiti kada s njim a završite.
48 Misliti na Javi

Zbog toga se postavlja važno pitanje. Ako Java ostavlja objekte da leže unaokolo, šta ih
sprečava 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č smeća (engl. garbage
collector); on vodi računa o svim objektima koji su napravljeni operatorom new i otkriva
one na koje više nema referenci. Zatim oslobađa m em oriju koju su zauzimali ti objekti, pa
ta m em orija može da se koristi za nove objekte. To znači da nikada ne m orate sami da se
brinete o oslobađanju memorije. Samo napravite objekte, a kada vam više nisu potrebni,
oni će sami otići. Ovo eliminiše 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 određuje kako određeni objekti izgledaju i kako se ponašaju? D ru-
gim rečima, šta uspostavlja tip objekta? Možete očekivati da postoji rezervisana reč ,,tip“,
i to bi svakako imalo smisla. H ronološki posm atrano, većina objektno orijentisanih jezika
koristila je rezervisanu reč class da naznači: „Upravo ću ti reći kako novi tip objekta iz-
gleda“. Tii rezervisanu reč (koja je toliko česta da neće 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 eđu tih znakova, o čem u će biti reči đalje u ovom poglavlju), pa malo šta
možete s njom da uradite. M eđutim, rnožete da napravite objekat ovog tipa koristeći new:

NekoI m e T i p a a = new N e k o I m e T i p a ( ) ;

Ali klasi ne možete reći da mnogo toga uradi (tj. ne možete joj poslati neke važne po-
ruke) sve dok ne definišete neke njene metode.

Polja i metode
Kada definišete lclasu (a sve što radite u Javi jeste definisanje klasa, pravljenje objekata tih
klasa i slanje poruka tim objektim a), u nju možete 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 možete da se povežete pu-
tem njegove reference. Reference na objekte m orate da indjalizujete (koristeći new, kao
što ste videli ranije), da biste ih povezali sa stvarnim objektima.
Svaki objekat ima zasebno skladište za svoja polja; obično se polja ne dele između 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 ništa, sem što čuva neke podatke. Ali ovako možete da napravite objekat:

SamoPodaci podaci = new SamoPodaci();

Poljima možete dodeliti vrednosti, ali prvo m orate da znate kako da se obratite članu
nekog objekta. To se postiže tako što se navede im e reference na taj objekat, zatim sledi
tačka 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 može da sadrži i druge objekte koji sadrže podatke. Samo nastavite da ,,na-
dovezujete tačke“.
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 može skoro ništa da uradi osim da čuva podatke, jer nem a me-
tode. Da biste shvatili metode, prvo m orate da naučite š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 pažnju na to da Java jamči dodelu podrazum evanih vrednosti samo
kada se promenljiva koristi kao članica klase. To obezbeđuje da prom enljive članice pro-
stog tipa uvek budu inicijalizovane (to C ++ ne radi), čime se sm anjuje izvor grešaka.
Ipak, ova početna vrednost ne m ora da bude ispravna niti odgovarajuća za program koji
pišete. Najbolje je da uvek izričito 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 neće 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 ljšan je u o d n o su na C + + : d o b ićete grešku 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 greškam 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 češće k o risti te rm in m etoda, kao „n ačin da se nešto u ra d i“. A ko želite, m o -
žete nastaviti da razm išljate o „funkcijam a", ali će se u ovoj knjizi nad alje k o ristiti u Javi
u običajen te rm in ,,m eto d a“.
M etode u Javi o d re đ u ju p o ru k e koje objekat m ože 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 vraća 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 ože 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 izvrši poziv. Ako po k u šate
da pozovete p ro g re šn u m e to d u objekta, d obićete p o ru k u o grešci p rilik o m prevođenja.
M eto du objekta pozivate tako što navedete im e olijekta iza koga je tačka, 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 vraća 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 ožete napisati
sledeće:

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 učiti, 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, prosleđujete 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 grešku.
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 gačak je 16 b itova, o d n o sn o dva
bajta, zbog p o d ržavanja 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 ožete ga tre tirati kao i svaki d ru g i objekat. (M ožete
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
vraća bro j znakova u znakovnom nizu.
O b ra tite pažnju na u p o tre b u rezervisane reči return koja obavlja dva zadatka. Prvo,
o n a znači „napusti m e to d u , završio 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 reči return. U ovom slučaju, p o v ra tn a v red n o st se
proizvodi izračun avanjem izraza s.length() * 2.
M ožete da vratite p o d ata k bilo kog tipa, ali ako ne želite n išta da v ratite, o n d a n azn a-
čite da m eto d a vraća 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 ožete vratiti iz bilo
koje tačke, ali ako ste naveli p ovratni tip koji nije void, prevodilac će vas, bez o bzira na m e-
sto sa kog vraćate, naterati (p o ru k am a o greškam a) da v ratite odgovarajući tip vred n o sti.
U ovom tre n u tk u m ožda 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 naučićete 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 - biće dovoljno.

1 Uz uobičajeno izuzim anje ranijc pom en utih prostih tipova podataka boolean, char, byte, short, int,
iong, float i double. I’o pravilu, vi prosleđtijete objekte, što znači da prosleđujete 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 sprečite 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 sadrži 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 oguće. D a bi se rešio taj p ro blem , u
C + + je p o m o ć u d o d a tn ih rezervisan ih reči uveden itnenski prostor (engl. namespace).
Z ahvaljujući n ov o m p ristu p u , Java je sve to m ogla da izbegne. Za p ravljenje je d n o -
zn ačn ih im en a za biblioteke, p ro je k ta n ti Jave žele d a ko ristite im e vašeg 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. Pošto 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 tačke 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 piše m alim slovim a.
O vaj m eh an izam znači da sve vaše 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.

Korišćenje 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 slučaju, sam o k o ristite klasu - čak i ako se o n a defim še tek kasnije u datoteci.
Java elim iniše 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 islićete 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 hoćete da
koristite klasu o d re đ e n o g im en a, ali za nju postoji više od jed n e definicije (p od pretp o-
stavkom da su različite). Ili još gore, zam islite da pišete p ro g ram i u svoju biblioteku do-
dajete novu klasu, čije se im e sukobljava sa im en o m neke postojeće klase.
Da biste rešili ovaj p ro b lem , m o rate da elim inišete sve potencijalne dvosm islenosti. To
se postiže tako što p o m o ć u rezervisane reči import, Java prev od io cu saopštite 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 ože da sadrži 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 ajčešće ć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 dugačkim , inv erto v an im im e n im a d o -
m en a; sam o napišete, 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 saopštili p rev o d io cu da želite d a k o ristite Javinu klasu ArrayList. M e đ u tim , p a-
ket util sadrži više kJasa i m o žete poželeti da ko ristite nekoliko n jih a d a ih p ri to m izričito
ne deklarišete. To se lako p ostiže o zn ak o m * za slo b o d an izbor:

import j a v a . u t i l

Skupove klasa češće ćete uvoziti na ovaj n ačin nego p o jed in ačn 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 tački se
o b je k tu d o d eljuje p ro s to r u m e m o riji (sldadište) 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 skladište 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 skladište 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 ožete da pozovete iako n ijedan o bjek at nije napravljen.
O b a ova efekta m o žete postići p o m o ć u rezervisane reči static. Kada nešto o značite kao
static, to znači 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 ožete 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 slučaju uob ičajen ih , n estatičn 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 znači 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 statičn e, stavite rezervisanu reč static p re defi-
nicije. Na p rim er, sledeći oblik proizvodi i inicijalizuje statičn 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 skladišta za p ro m en ljiv u StaticTest.i. O ba objekta će deliti istu p ro m e n -
Ijivu i.

N aravno, pošto 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 nestatičnim članicam a ili m etodam a. Pošto nestatični članovi
i m etode m oraju biti povezani sa odgovarajućim objektom , iz statičnih m etoda m ožete pristupati ne-
statičnim članovim a sam o preko nekog im en ovanogobjekta.
54 Misliti na Javi

R azm o trim o sledeće:

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 način a za p ristu p statičn 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 ožete 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 + + uvećava 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 aće i v red n o st


48.
N avođenje im en a klase je poželjniji n ačin za p ristu p a n jc statičnim članovim a. Ne
sam o d a se tim e naglašava n jih o v a sta tičn a p riro d a , n ego se u n ek im slučajevim a prevo-
d io c u daje veća m o g u ćn o st za o p tim izo v an je.
Slična logika se p rim en ju je i n a statičn e m eto d e. S tatičnoj m e to d i m ožete 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(). Statična m eto d a se definiše na sličan način:

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 pažn ju na to da u klasi MozeSeUvecati m eto d a uvecaj() p o m o ću o p erato ra


+ + uvećava statičan p o d atak i. M eto d u uvecajf) m ožete pozvati na uobičajen način, pre-
ko objekta:

MozeSellvecati sp = new MozeSeUvecati ( ) ;


s p .u v e c a j( ) ;

Pošto je uvecaj() statična 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 način na koji se
p o d a tak stvara (po jed an za svaku klasu u o d n o su na po jedan nestatičan 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 označavaju rezer
visan o m rečju static kako bi se m ogle pozvati iako nije naprav ljen objekat. O vo je veom a
važno, kao što ćem o videti, za đ efinisanje m eto d e main() koja je p o četn a tačka za pokre-
tan je svih aplikacija.

Vaš prvi program na Javi


K onačno, evo prv o g p o tp u n o g p ro g ram a. O n počinje ispisivanjem znakovnog niza, i da-
tu m a, koristeći 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 videćete 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 ićete listu svih klasa koje su deo te biblioteke. Pošto je paket java.lang im pli-
citn o uključen 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 znači 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. Sađa ćete videti sve klase Javine sta n d a rd n e biblioteke.
Z atim m ožete iskoristiti funkciju čitača ,,find“ da p ro n a đ ete klasu Date. Kada to urad ite,
videćete da se o n a na listi nalazi kao java.util.Date, što znači 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 početak, izaberete java.lang i zatim System, videćete da ldasa Systern
im a nekoliko poija, a ako o d ab erete out, o tk rićete da je to statičan objekat ldase Print-
Stream. Pošto je on statičan , ne m o ra te ništa da p rav ite p o m o ć u new. O bjekat out je uvek
tu i m ožete ga jednostavno k oristiti. O n o što m ožete da rad ite sa ovim objektom out, o d-
ređeno je njegovim tip o m : PrintStreani. P rak tičn o sti radi, PrintStream je u opisu p rika-
zan kao hiperveza, stoga ako ga p ritisn ete, videćete listu svih m eto d a koje m ožete pozvati
za tu klasu. Njih im a relativno m n o g o i biće o b jašn jen e dalje u knjizi. Zasad nas zan im a
sam o println(), što znaći „ispiši 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 nešto želite da ispišete 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 sadrži m etodu pod im en o m main() sa sledećim 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 t’ntacija stalno se m enjaju i najbolje ih je preuzim ati neposredno
od Suna. Ako je sam i preuzm etc, dobićete najnoviiu verziju.
56 Misliti na Javi

R ezervisana reč public označava 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 eštaju 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 riličn 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 završi, taj
objek at više nije p o tre b a n i sakupljač sm eča m ože 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 raču n a o njeg o v o m čišćenju.
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 , videćete 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 izvođenje zan im ljiv ih efekata. (Jedna
o d najvećih 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
izvršavate 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 idećete u n astavku knjige da ih m ožete poslati i na d ru g a m esta,
recim o u neku d ato tek u . M ožete zatražiti i o d re đ e n o svojstvo - u ovom slučaju, 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 jasnićem o n eo b ićn e k o m en tare na p o četk u i na kraju.)

Prevođenje i izvršavanje
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 ružen je za Javu. Postoji više nezavisnih razvojnih o k ru žen ja, ali p retp o -
stavićem 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 raćete 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 pokrećete p ro g ram e.
Povežite se na In te rn et i p o gledajte W eb lokaciju http://iava.suii.coin. Tam o ćete naći
in form acije i veze koje će vas voditi kroz proces p reu zim an ja i in staliran ja )D K -a za vašu
p latfo rm u .

O bičn o je to IBM -ov prevodilac jikes, koji je zn atn o brži 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, izvršnih okruženja 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 naći 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 upišite:

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 grešci, znači 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 ićete p o ru k u i d atu m .


N avedeni proces m ožete 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 , videćete 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 sadrži ,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 naći 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 biće 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 ručn o.

Komentari i ugrađena 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 uće i u više redova,
sve d o */. O b ra tite pažnju 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 */ ignoriše, stoga n em a razlike ako


napišete:

/* 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 potiče iz C + + -a. To je k o m e n ta r u je d n o m redu , koji počinje


sa // i nastavlja se d o kraja reda. Ovaj način k o m en tara je p rak tičan i često se koristi jer je
jed n o stav an . Ne m o ra te da lovite p o tastatu ri i da tražite / 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 najveći p ro b lem p ri d o k u m e n to v a n ju koda bilo o d ržavanje 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. Rešenje izgleda jed n o stav n o : povežite k o d i d o k u m e n ta c i-
ju . N ajlakše ćete to u ra d iti kad sve stavite u istu d ato tek u . D a biste zaokružili 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 traži posebne oznake k o m en tara u p ro g ram u . Izdvaja
označene inform acije i izvlači im e klase ili m etode koja ide uz taj kom entar. Na ovaj način
m ožete sa m in im aln o m količinom 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 ožete da p regledate p o m o ć u čitača
W eba. Tako Javadoc om ogućava d a na p ra v ite i održav ate sam o je d n u izv o rn u d ato te k u i
a u to m atsk i generišete k o risn u d o k u m en taciju . Z ahvaljujući 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 hoćete p o seb n o da o b rad ite (recim o, da ih
ispišete u d rug ačijem 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 obeležja. P o tp u n opis naći ć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 isključivo u n u ta r k o m entara /**. K om entar se za-
vršava sa */ kao i obično. Postoje dva osnovna načina za korišćenje ovog sistem a: ugrađeni
H TM L ili korišćenje d o k u m en tacio n ih oznaka (engl. doc tags). Samostojeće 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 početku reda kom entara. (Zane-
m aruje se vodeća 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 počinju sa @, ali se nalaze izm eđu vitičastih 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 ažn ju na to d a će Javađoc 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 zaštićene č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 rišu se i nećete 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 uključili i privatne članice.) O vo im a sm isla,
p o što su sam o javne i zaštićene č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 oći
će lako da se kreću 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.

Ugrađeni 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 generiše. To
o m og u ćava p o tp u n o korišćenje 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 {}

Takođe, H TM L m ožete 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 običan tekst u svojim opisim a:

//: object/Documentation3.java
/**
* Možete u b aciti <em>čak</em> i li s t u :
* <ol>
* <li> Prva tačka
* <1i> Druga tačka
* <1i> Treća tačka
* </ol>
*/
pu b lic c lass Documentation3 {}

O b ra tite pažnju 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 početk u reda, kao i vodeće 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 vaši ć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
posvećen odeljak u d o k u m e n ta ciji JD K -a i tam o vid ite sve m oguće način e u p o tre b e
Javadoca.

@see:
O va o zn ak a služi 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 slično o znaci @see, sem što se m ože 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 izričito
h iperpovezivanje sa stra n ic a m a u stablu do k u m en tacije.

{@inheritDoc}
Tekući d o k u m en tac io n i k o m e n ta r n asleđuje d o k u m en tac iju najbliže 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 uključiti. 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 vaše im e ali m ože da b u d e i vaša 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 biće p rosleđena u g en erisan u
H T M L d o k u m en tac iju .
M ožete im ati više 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 biće spojene u jed an pasus u g enerisanom H TM L-u.

@since
O va oznaka o m og u ćava da naznačite verziju klase koja je počela da k oristi o d re đ e n u m o -
gućnost. V idećete da se pojavljuje u Java H T M L d o k u m en taciji da naznači koja je verzija
JD K -a korišćena.

©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 ože
p ro te g n u ti na nekoliko redova. S m atra se da je opis završen kada se naiđe n a n o v u d o k u -
m e n ta c io n u o zn aku . M ožete im ati koliko ho ćete ovih oznaka, najčešće 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 piše se u obliku:

Pretu rn opis

gde o p is daje značenje p o v ra tn e v red n o sti. O n m ože da se p ro teg n e na nekoliko redova.

@throws
O izuzecim a g o v o rim o u poglavlju O brada grcšaka pom oću izuzctaka. U kratko, to su
objekti koji m og u biti ,,bačeni“ iz m eto d e u slučaju greške. Iako sam o je d an objekat izu-
zetka m ože d a se pojavi kada pozovete m eto d u , o d ređ en a m eto d a m ože da proizvede više
razlićitih tipova izuzetaka a svi m o raju biti u n a p red naznačeni. Stoga oznaka za izuzetak
im a sledeći 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 ože da se p ro teg n e na nekoliko redova) pokazuje zbog čega odre-
đeni tip izuzetka m ože da se pojavi pri pozivu inetođe.
62 Misliti na Javi

©deprecated
O vo se koristi da naznači m o g u ćn osti koje su zastarele. O znaka @deprecated pokazuje da
više ne treba da koristite tu m ogućnost, p o što će uskoro v erovatno biti u klonjena. Prevodi-
lac će vas upozoriti ako koristite m e to d u označenu sa @deprecated. U Javi SE5, @depreca-
ted je zam enjena anotacijom @Deprecated (anotacijam a je posvećeno 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 današnji datum.
* @author Bruce Eckel
* @author www.MindView.net
* @version 4.0
*/
public class Zdravo {
/** Ulazna tačka 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 sadrži ime izvorne datoteke. Taj red sadrži inform aciju o p utanji do da-
toteke (object označava ovo poglavlje), iza koje sledi ime datoteke. Poslednji red takode se
završava kom entarom ( / / / : - ) koji označava kraj listinga izvornog koda i om ogućava da se
taj kod autom atski ažurira u tekstu ove knjige (nakon što ga prevodilac p ro v eri) i da se izvrši.
O znaka /* Ispis: naznačuje početak izlaza koji će ovaj p ro g ram generisati. U ovom obli-
ku, (55% nratch) naznačuje sistem u za testiranje da se izlaz priličn o razlikuje od jed n o g do
drugog izvršavanja i da treba da očekuje 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, većina će sadržati ovaj
oblik izlaza, pa ćete m oći da izvršite 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 reči, o n e se
pišu 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
reči je veliko, na p rim er:

c lass SveBojeDuge { // . . .

Tako n ap isano im e klase liči 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 piše 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 đugačka 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-
tičastih zagrada p o p u t stila u p o treb ljen o g u ovoj knjizi.

Sažetak
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 nešto treće“. U n a re d n a dva p o-
glavlja upoznaćete 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
naučićete da upravljate tok o m p ro g ram a.

Vežbe
U b ud uće će vežbe biti raspodeljene po celom tek stu poglavlja, ali u ovom su sve vežbe sta-
vljene na kraj, pošto ste tek naučili da pišete najjed n o stav n ije p ro g ram e.
Iza red n o g broja vežbe nalazi se broj u zag rad am a, koji u op seg u od 1 do 10 pokazuje
njenu težinu.
Rešenja o d a b ra n ih vežbi 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 ože 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 previše prosfora u knjizi i na prezentacijam a, ali uverićete se da ovde upotrebljeni
stil odgovara stan d ardu Jave koliko god je to m oguće.
64 Misliti na Javi

Vežba 1: (2) N apišite klasu koja će sad ržati neinicijalizovana p o lja tip a int i char, i ispišite
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.
Vežba 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 izvršava kada se p ro g ra m p o k re n e). Setite se da tre b a d a o stan e statična i da
uključite listu a rg u m en ata, iako je nećete 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 ačije od
JD K -a, n au čite kako d a prevedete i izvršite p ro g ra m e u to m o k ru ž e n ju .
Vežba3: (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 ožete d a p revedete i izvršite.
Vežba 4: (1) P retvo rite deliće k od a koji o b u h v ataju klasu SamoPodaci u p ro g ra m koji
m ožete d a prevedete i izvršite.
Vežba 5: (1) Izm enite p re th o d n o vežbanje 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().
Vežba 6: (2) N apišite 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.
Vežba 7: (1) Pretvorite MozeSeUvecati delove k o d a u p ro g ra m koji radi.
Vežba 8: (3) N apišite 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
statičnog polja u klasi, bez o b zira na to koliko objekata te klase n apravite.
Vežba 9: (2) N apišite p ro g ra m koji po k azu je da a u to m atsk o pakovanje fu n k cio n iše za sve
pro ste tipove i njihove o m otače.
V ežba 10: (2) N apišite 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.
Vežba 11: (1) Pretvorite p rim er SveB ojeD uge u pro g ram koji m ožete da prevedete i izvršite.
Vežba 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 čitača W eba.
Vežba 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 čitača W eba.
V ežba 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 vežbanju.
Vežba 15: (1) U zm ite p ro g ra tn iz vežbe 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 koristeći Jav ad o c i pregledajte
ih p o m o ć u čitača W eba.
V ežba 16: (1) U poglavlju Inicijnlizacijn i čišćenje 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 koristeći Jav ad o c i p regledajte ih p o m o ću čitača W eba.
Operatori
N a n ajnižem nivou, u Javi se s podacim a radi pom oću operatora.

KAKO JE JAVA IZVEDENA IZ C + + - A , VEĆINA OVIH OPERATORA BIĆE POZNATA C I C + +


p ro g ram e rim a . U Javi su d o d ata neka pob o ljšan ja i pojednostavljenja.
U koliko poznajete sin tak su C -a ili C + + -a, m o žete p releteti kroz ovo i sledeće poglavlje
i zadržati 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 ožete b esp latn o preuzeti s Web lokacije w w w .m in d view .n et. O n sadrži a u d io lekcije,
slajdove, vežbe i rešenja iz kojih se m o g u steći 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') ;

I’rim etićete da tu im a ne sam o p riličn o m n o g o k u can ja (i suvišnog n a p o ra za tetive),


nego je i p rilično nerazum ljivo kada se p ročita. Većina 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 eđu tim , ne m o rate znati te p o jed in o sti da biste m ogli da je upotreb ljav ate. Ko-
risteći je, p rerad ićem 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 ažnju 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 aširite 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 stići i do p o tp u n o g opisa p u ta n je klasa (engl. classpath), ali neće škoditi
66 Misliti na Javi

ako o d m a h počnete d a se privikavate n a b o rb u s n jo m . N ažalost, s Javom ćete m o rati


počešće d a se borite.
Iako u p o tre b a biblioteke net.mindvievv.util.Print lepo p ojeđnostavljuje većinu p ro -
gram a, ne m ože se o p rav d ati n jen o korišćenje baš posvu da. U koliko u p ro g ram u im a
m alo n a red ab a za ispisivanje, p resk ačem uvoz (engl. irnport) te biblioteke i pišem dugački
System.out.println ().
Vežba 1: (1) N apišite p ro g ra m u k o jem up otreb ljav ate i skraćen i i n o rm a ln i oblik na-
redaba za ispisivanje.

Korišćenje operatora u Javi


O p e ra to r k o m b in u je je d a n ili više a rg u m en ata i daje no vu v red n o st. A rgu m enti se zadaju
u obliku drugačijem o d uobičajen 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 oženje (*), deljenje (/) i do dela v red n o sti (=) m anje-više 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 ože 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 najčešće 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-
država i o p e rato re + i +=.

Priori teti
P rioriteti o p erato ra definišu kako će izraz biti izraču n at kada se u n jem u javlja vi.še ope-
rato ra. Java im a specifična p ravila koja o d re đ u ju p o red ak izračunavanja. Najlakše se
p am ti pravilo da se m n o žen je i deljenje vrše 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-
ričito navođenje p o retk a izračun 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 različita značenja.
O b ra tite pažn 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 , + znači „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 šaće d a k o n v ertu je ne-String u
String. Kao što vid ite iz izlaza, a i b je u sp ešn 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 znači „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 ože d a se iz-
račun 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 fizički p ro sto r za sm eštan 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 išta da d o d elite - o n a n e m ože b iti Ivrednost.
(N e m o žete n apisati 4 = a;.)
D odela v red n osti prosto g tip a je p riličn o očigledna. Pošto 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 sadržaj s jed n o g m esta na d ru g o . Na p rim er, ako napišete a = b za proste tipove,
o n d a se sađržaj p rom enljive b ko p ira u sadržaj pro m en ljiv e a. Ako n akon toga nastavite
da m en jate a, ta izm ena, narav n o , neće uticati na b. Kao p ro g ram er, u većini situacija ovo
i očekujete.
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 znači da će, kada napišete 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 ponašanje:

//: operatori/D odela . java


// Dodela vrednosti objektima može 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 različite 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 očekivali 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 sađrže 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 eća).
O vaj fenom en često se naziva pojavap scu d o n im a (engl. aliasing), i on je u o snovi način
na koji Java rad i sa o b jek tim a. Ali šta ako nećete da se u ovom slučaju pojavi pseu d o n im ?
M ožete da precizirate d o d elu i d a napišete:

r l.n iv o = r2 .n ivo ;

Ov'ako će se zadržati dva o dvojena objekta, u m esto da jed an b u d e o d b ačen i da se obe


reference povežu 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 ože da dovede d o iznenadenja.
V ežba 2: (1) N apišite klasu koja sadrži broj tip a flo at i p o m o ć u nje pokažite pojavu
p seu d o n im a.

Pojava pseudonima pri pozivanju metode


P seu d o n im će se takođe pojaviti kada objekte prosleđujete m eto d am a:

/ / : op eratori/Prosl edi O bjekat. java


// Pro sleđ ivan je objekata metodama može 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 važenja m etode. Ali, pošto 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 rešavanje je složeno; 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.
Vcžba 3: ( 1) N apišite klasu koja sadrži broj tipa flo at i p o m o ć u nje pok ažite p ojavu
p se u d o n im a p rilik o m pozivanja m etoda.

Matematički operatori
O sn ov ni m atem atičk i o p e ra to ri su isti kao i u većini 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 skraćeni C /C + + zapis za isto v rem en o ob avljanje o p eracije i dođele.
To se naznačava na sledeći način: iza o p erato ra sledi znak jedn ako sti, i d o sled n o se m ože
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, pišite: x + = 4.
O vaj p rim e r p okazuje korišćenje m atem atičk ih op erato ra:

/,/: o peratori/M atO peratori. java


// Prikaz matematičkih 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 ) ;
// Sledeće 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 tekuće 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 izvršavanja p ro -
g ram a daje drugačiji izlaz. M eđ u tim , u ovoj knjizi važno je da izlaz p rik azan n a k raju p ri-
m era b u d e što u jednačeniji, 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 seudoslučajni brojevi b iti generisani svaki p u t, p a se iz-
laz p ro g ra m a m ože p ro v e riti1. U slučaju da želite drugačiji 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 generiše razne tipove slučajnih brojeva p o m o ć u objekta klase Randoni, p o -
zivajući 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
nećem 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 ežba 4: (2) N apišite p ro g ra m koji izračunava 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 načina kako pišete izraz. Na p rim er, zn a-
čenje izraza

x = -a ;

o čig led n o je. Prevodilac m ože da raspozna:

x = a * -b;

ali bi čitalac m ogao da se zbuni, stoga je k atkada jasnije ako se napiše:

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 koledžu koji sani pohađao, pa m i se to urezalo u sećanje.
72 Misliti na Javi

Automatsko uvećanje i umanjenje


Java im a p u n o skraćenica, kao i C. Skraćenice d o p rin o se lakšem p isan ju k o d a i lakšem , ili
težem , čitanju koda.
Dve zgodne skraćenice su o p e ra to ri uvećanja 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 označava „sm anji za jedi-
n icu “. O p e ra to r uvećanja je + + i o značava „povećaj 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 uvećanje i
u m an jen je m en jaju v red n o st pro m en ljiv e, nego je i v raćaju kao rezultat.
Postoje dve verzije oba o p erato ra, koje se često nazivaju prefiksne i sufiksne verzije. Pre-
fiksno uvećanjeje oblik kada se o p e ra to r + + pojavljuje p re prom enljive, a sufiksno uvećanje
kada se o p erato r + + pojavljuje n ak o n p rom enljive. Slično 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 uvećanju 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 vraća nova v red n o st. Pri sufiksnom uvećanju i
sufiksnom u m an jen ju (tj. a++ ili a—), prvo se vraća stara v red n o st, a zatim se izvršava 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 uvećanje
p rin t("i+ + : " + i++); // Sufiksno uvećanje
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 vraća n ak o n izvršavanja operacije, ali za sufik-


sni oblik dobijate vred n o st p ro m en ljiv e pre nego što se o p eracija izvrši. O vo su jedini ope-
rato ri (osim o n ih koji u pliću 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 objašnjenja za p o rek lo im en a C + + , koje znači „jed an k o rak dalje o d C -a“, leži
u o p e ra to ru uvećanja. 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), nagoveštavajući kako je Java jezik C + + iz
kojeg su izbaćeni n e p o tre b n i teški delovi, pa je zbog toga m n o g o jednostavnija. Što b u -
dete više napred ovali kroz ovu k njigu, videćete 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 poređenja
R ezultat o p e ra to ra p o ređ en ja je logička 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 tačn 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 (< ), veće od
(> ), m an je ili jed n ak o (< = ), veće ili jed n ak o (> = ), jed n a k o (= = ) i različito (!=). Jednakost
i različitost 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 veći, 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 značenje često
zb u n ju je p očetnike 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 logičkog 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 sledećeg false,
pošto su v redno sti oba objekta klase In te g e r iste. Ali iako je sadržina o b jekata ista, refe-
rence su različite, a o p e rato ri = = i != p o red e reference objekata, a ne n jihovu sadržinu.
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 sadržaja objekata? U to m slučaju 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 išu = = 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 očekujete. 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 sadržinu.

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 ašan je m etode
equals() da poredi reference. Stoga n e ć e ted o b iti željeno p o n ašan je osim ako ne rcdefinišeie
(engl. override) m etodu equals() u vašoj novoj klasi. N ažalost, o redefm isanju nećete učiti
sve do poglavlja Ponovno korišćenje klasa, a o p rav iln o m načinu 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 onaša, jer će vas to m ožda sačuvati od problem a.
Većina klasa iz Javine biblioteke realizuje m eto d u equals() tako da o n a p oredi sadržaj
objekata u m esto njihovih referenci.
Vežba 5: (2) N apravite klasu Pas koja sadrži dve prom enljive tipa String: ime i kaže. U
m etod i main() n apravite dva objekta tip a Pas sa im en im a Fleki (koji k aže:„R afi“) i Buva
(koji kaže: ,,Vafi“ ). Z atim p rik ažite njihova im ena i šta kažu.
Vežba 6: (3) Posle vežbe 5, napišite 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 koristeći = = i equals() za sve reterence.
Poglavlje 3: Operatori 75

Logički operatori
Logički 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 logički o p erato ri:

//: o p e ra to ri/ L o g ick i. java


// R elacion i operatori (poređenja) 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 može 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 ožete p rim e n iti sam o na v red n o sti tip a
b o o le an . Za logičke uslove ne m ožete da koristite ostale (nelogičke) tipove, kao u jezici-
m a C i C + + . O vakve neuspele pokušaje m ožete v ideti u k o m e n ta rim a sa o zn ak o m //!
76 Misliti na Javi

(takvo označavanje k o m e n ta ra om o g u čav a n jihovo au to m atsk o uklanjanje, što olakšava


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 logičke 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-
juči tek stualni oblik ako se k oristi ta m o gde se očekuje objek at klase String.
D efiniciju za int u p re th o d n o m p ro g ra m u m ožete 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
,,različit“. Broj koji je na n ajm an jo j d ecim ali iznad nule i dalje je „različit o d n u le“.
Vežba 7: (3) N apišite p ro g ram koji sim u lira b acanje novčića („pism o - glava“ ).

Nepotpuno izračunavanje
K ada rad ite s logičkim o p e ra to rim a , naići ćete na fen o m en p o d nazivom „ n e p o tp u n o iz-
raču n av an je“. To znači da će izraz b iti izraču n av an sam o do tren u tk a kada tačn o st ili ne-
ta čn o st celog izraza m ože n ed v o sm islen o da se odredi. Z ato p reostali delovi logičkog
izraza u opšte neće b iti izraču n ati. Evo p rim era koji p o kazuje n ep o tp u n o izračunavanje:

//: operatori/N epotpunoIzracunavanje.java


// Prik azuje pojavu nepotpunog izračunavanja pri radu s logičkim 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 logičku 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 logičku vrednost fa ls e
* ///:-
Poglavlje 3: Operatori 77

Svaki test vrši p o ređ en je sa a rg u m e n to m i vraća v re d n o sti true ili false. T akođe ispi-
suje i p o ru k u da je bio pozvan. Testovi se pozivaju preko sledećeg 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 izvršena, ali izlaz pokazuje drugačije:
Prvi test daje rezultat tr u e pa se izračunavanje izraza nastavlja. M eđutim , drug i test daje re-
zu ltat false. Pošto to znači da će i rezultat celog izraza sigurno biti false, zašto nastavljati iz-
računavanje izraza? To m ože da potraje. Baš zbog toga se koristi n e p o tp u n o izračunavanje:
ako ne treba do kraja izračunavati sve delove logičkog izraza, p ro g ram će m ožda raditi brže.

Literali
Kada u p ro g ra m u doslov n o n avodite v ređ no sti, prevodilac o b ičn o tačn o zna kojim tip o m
da ih p redstavi. P o nek ad se m ože javiti n ed ou m ica. U to m slučaju 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.
Sledeći 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 (vodeća 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 ; // najveća 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; // najveća 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; // najveća 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 (može 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 označava tip long (m eđ u tim , m alo
1 zb un juje zato što liči n a jed in icu ). Veliko ili m alo F označava tip float. Veliko ili m alo D
označava tip double.
Sve celo b ro jn e tipove p o d atak a m oguče je zadavati u h ek sad ecim aln o m obliku (sa
o sn o vom 16), n azn ačav an jem v o d ečim 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 većom
od one koju m ože d a čuva (bez o b zira na n u m eričk i oblik v red n o sti), prevodilac če pri-
javiti grešku. U g o rn jem p rim e ru o b ra tite p ažn ju na m aksim alne m oguće heksadecim al-
ne v red n o sti za tipove char, byte i short. Ako ih p rekoračite, prevodilac če ih auto m atsk i
p retv o riti u int i jav iti kako treb a izvršiti sužavanje 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
prekoračili dozvo ljen u v red n o st.
O ktalni ob lik (sa o sn o v o m 8) nazn ačav a se v odećom 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 eđutim , 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 postiže 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().
Vežba 8: (2) Pokažite 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" znaći "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 inžen jerstv u ’e’ o značava 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 znači 1,39 x 2,718‘43. T vorci FOR-
T R A N -a odlučili su d a če e značiti „deset na step en “, što je ču dn o, je r se FO RTRAN u p o -
trebljava u nauci i inženjerstvu, 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 običaj 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 značava 1,39 x 10~43.
P rateći znak n e m o ra te da k o ristite kada prevodilac m ože sam da o d red i odgovarajući
tip. Kada napišete:

long n3 = 200;

nem a dvosm islenosti, pa je L iza bro ja 200 iziišno. M eđ utim , kada napišete:

flo a t f4 = le-43f; // 10 na -43. stepen

prev od ilac o b ičn o tu m ači 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 ratećeg slova f prijav io grešku, tražeći eksplicitnu konverziju iz
d o u b le u float.
Vežba 9: (1 ) Prikažite najveći i n ajm an ji b ro j koji se m ože 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 logičku (B ulovu) algebru
nad od go varaju ćirn bito v im a dva a rg u m e n ta da bi izračunali 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 nećete pre-
više koristiti.
O p e ra to r konjunkcije nad bitovim a (&) daje jedinicu kao vrednost izlaznog bita ako su
oba ulazna bita jednaka jedinici; inaće, 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 ačunarim a sam počeo da se bavim 1962. koristeći FORTRAN II na m aši-
ni IBM 1620. U to vrem e, tokom šezdesetih i početkom sedam desetih, FORTRAN je u p o tp u n o sti
pisan velikim slovim a. To potičc verovatno od toga što su stari uređaji 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 ešano sa osnovom p riro d n o g logaritm a, e, koja se uvek piše m alim slovom .
H je označavalo eksponent, obićno 10, za korišćeni brojni sistem. U to vrem e i oktalni form at je bio
široko rasp ro stranjen m edu p rogram erim a. Da sam naišao na oktalni broj u eksponencijalnoj nota-
ciji, sm a trao bih da je osnova 8. Sećam se da sam p rvi p ut video eksponencijalni zapis napisan s
m alim e u kasnim sedam desetim i sm atrao sam ga zbunjujućim . Froblem se pojavio kada su m ala
slova p ro d rla u FORTRAN, ne na saniom poćetku. 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. Isključiva 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 takođe 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 logičke o p e rato re k o riste se isti sim boli, p a će v am biti
lakše da se setite značenja ako se d o setite sledećeg: 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=. (Pošto je ~ u n a rn i o p erato r, o n ne m ože 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 nešto d rugačija. N ad
njim m ožete da p rim e n ite k o n ju n k ciju , d isju n k ciju i isključivu 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 ešanje s logičkom negacijom ).
Za tip boolean o p e rato ri n ad b ito v im a im aju isti efekat kao i logički o p e ra to ri, osim što
se n e javlja n e p o tp u n o izračunavanje. O p e ra to r isključive disju n k cije n ad b ito v im a nem a
ekvivalentan logički op erato r. Z ato ovaj o p e ra to r predstavlja jed in i način 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 ožete
p rim en jiv ati o p erato re p o m e ran ja koje ćem o u p rav o opisati.
Vežba 10: (3) N apišite p ro g ram s dve b in a rn e k o n stan te koje im aju n aizm en ičn e jedinice
i nule, s tim što je prvoj na n ajm an je zn ačajn o m m estu nula, a d ru g o j jed in ica. (U putstvo:
to će vam biti najlakše 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 naćine, a rezu ltate prikažite m eto d o m
Integer.toBinaryString().

Operatori pomeranja
O p erato ri p o m e ran ja (engl. shift) takođe 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
(niži bitovi p o p u n jav aju se n u lan ia). O p e ra to r o zn ačen 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 ačen o p o m eran je ud esn o > > k oristi produžavanje uz očiivanje znaka
(engl. sign extension)\ ako je v red n o st p ozitivna, viši bitovi se p o p u n jav aju nu lo m ; ako je
v red n o st negativna, viši bitovi se p o p u n jav aju jedinicom . U Javu je takođe d o d a to i neo-
značen o p o m e ran je u d esn o » > koje k oristi produžavanje nz dodavanje nula (engl. zero
extension): bez o bzira na znak, viši 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 izvrši p o m eran je i rezu ltat će biti tip a int. Koristi se sam o p et nižih b itova vrednosti
s desne stra n e o p erato ra. O vo vas sprečava da izvršite p o m eran je za više m esta nego što
ima bitova u ru ita r p ro m en ljiv e tip a int. Ako o perišete n ad v red n o šću tipa long, dobićete
Poglavlje 3: Operatori 81

rezu ltat tipa Iong. Biće k orišćeno sam o šest nižih b ito v a v re d n o sti s desne stra n e o p era-
to ra, pa ne m ožete da izvršite p o m e ra n je za više m esta nego što im a b itova u n u ta r p ro -
m enljive tip a long.
P om eranje m ože 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 ačen 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, nećete 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 raću ju p riiik o m d odele, pa u tim
slučajevim a kao rezultat d obijate —1. To pok azuje n a red n i p rim er:

//: operatori/NeoznacenoPomeranjeUdesno.java
// Test za neoznačeno 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 korišćenje svih o p e ra to ra koji se o d n o se n a bitove:

//: operatori/RadSaBitovim a.java


// Korišćenje 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 ožete videti
kako o n e izgledaju. O b ra tite p ažn ju n a to da najviši b it p redstavlja znak: 0 označava p o -
zitivan broj, a 1 negativan. P rik azan je rezu ltat p ro g ram a za deo koji se tiče tip a int.
O vakav b in arn i zapis celih b rojeva naziva se drugi kom plem ent.
Vežba 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 ajznačajnijem 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
označenog 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 prikažite
m e to d o m Integer.toBinaryString().
Vežba 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 ačen o g p o m e ra n ja p o m e ra jte u d esn o p o svim m o -
gućim b in a rn im p o lo žajim a i sve ih p rik ažite m e to d o m Integer.toBinaryString().
Vežba 13: (1) N apišite 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 prikažite n a n ek o lik o različitih znakova.

Ternarni operator uslovljavanja


Ternarni o p e ra to r uslovljavanja n eo b ićan 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 uobičajene n ared b e if-else koju ćete videti u
sledećem 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 logičkog-izraza tr u e , izračunava se vrednostO i taj rezultat postaje vred-


nost operacije. Ako je v red n o st logičkog-izraza false, izraćunava se vrcdnostl i njen rezul-
tat postaje v redn o st cele o p eracije.
N aravno, m ožete da k o ristite i u o b ićajen 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 sažetiji. Iako je jed n a od n ajistak n u tijih osobina jezika C (iz
koga ovaj o p e ra to r potiče) m o g u ć n o st pisanja saž.etih izraza i iako je tern arn i o p e ra to r
delim ićno uveden zbog efikasnosti, treb a biti o p rezan pri njegovom sv akodnevnom ko-
rišćenju jer lako m ože da pro izv ed e nečitljiv 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 izraćunava
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() sažetiji 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
lakše čita i brže piše. 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 način
koriščenja tih o p e rato ra, iako se ne u k lap a u trad icio n aln i način njihovog korišćenja.
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 ogućilo dodavanje značenja skoro
svakom o p e ra to ru . N ažalost, preklapanje o p erato ra je, u kom binaciji s nekim d ru g im
o g raničenjim a C + + -a , ispalo p rilično 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 gućnost je i dalje ocenjena kao previše složena, 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 korišćen 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 ); // Skraćeni 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 pažn 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 vodeću 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đ izraču 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 izričitu m eto d u , u ovom slučaju, Integer.toString().

Česte greške prilikom korišćenja operatora


N ačinićete je d n u od čestih grešaka 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 teći izraču n av an je izraza. O vo važi i u Javi.
Veoma česta greška 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 vrši dodelu. U C -u i


C + + -u rezultat ove d o d ele bi uvek b io true, ako je y razlićito od nule, i verovatno biste
dobili b eskrajn u petlju. R ezultat ovog izraza u Javi nije tipa boolean, ali prevodilac oče-
kuje tip boolean i neće izvršiti konverziju iz tipa int, nego će p ri prevođenju
prijaviti grešku. 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 rešku p ri p rev o d en ju jed in o nećete 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 grešku u k u can ju .)
Sličan p ro blem u C -u i C + + -u jeste korišćenje k o nju n k cije i đ isjunkcije nad bitovim a
um esto logičkih varijanata. K onjunkcije i disju n k cije nad b ito v im a se pišu s jed n im sim -
bolom (& ili I), dok se logička k o n ju n k cija i d isjunkcija pi.šu s dva (&& i II). Isto kao i sa
= i = = , lako je greškom upisati sam o jed an znak um esto dva. Javin prevodilac to ponovo
sprečava, jer neće dozvoliti da iz n eh ata iskoristite pogrešan 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 ože i da zahteva eksplicitnu konverziju
(engl. casting) ili da je sprovede u slučajevim a u kojim a se n o rm a ln o ne bi dogodila.
D a biste izvršili 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;
// "Sužavajuća 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 oguće izvršiti na nu m eričk o j vred n o sti, kao
i na p rom enljivoj. Im ajte u vidu da je đozvoljena i izlišna 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-
višna konverzija se dozvoljava, je r tim e m ožete d a ie naglasite ili da kod u činite jasnijim .
U d ru g im situacijam a, eksplicitna konverzija m ože 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 ože izazvati m alo glavobolie. U Javi je a u to -
m atska konverzija b ezb ed n a osim kada vršite takozvanu snžavajuću konverziju (tj. kada
prelazite sa tipa p o d atak a koji m ože da čuva više inform acija na onaj koji čuva m anje), pri
čem u rizikujete da izgubite inform acije. U to m siučaju prevođilac vas prisiljava da vršite
eksplicitn u konverziju, obaveštavajući vas na taj način da ,,to m ože biti op asn o - a ako
ipak to želite, m oraćete izrićito da tražite konverziju“. Proiirujuću konverzijn ne treba iz-
rićito zahtevati jer novi tip m ože da ćuva z n a tn o više in fo rm acija nego stari, pa se infor-
m acije nikad ne gube.
Java dozvoljava da izvršite 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 ožete k o nvertovati. Klase takođe 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 videćete i da se m ože vršiti eksplicitna
konverzija objekata u o k v iru porodice tipova; Hrast m ože 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 zaokruživanje
K ada obavljate sužavajuću 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 izričito 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 - hoćete 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 sledećem 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 dešava 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 oćete 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

Pošto 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 uvoženje i korišćenje.

Unapređenje tipova
Ako vršite bilo koju m atem atičk 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), videćete 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 izvrše, 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 pošto vršite d o d elu m an jem tip u , m o gu se izgubiti inform acije.) U prin ci-
p u , najveći tip p o datak a u izrazu o d ređ u je veličinu 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 određivanje veličine


U C -u i C ++-U o p e ra to r sizeof() zadovoljava specifičnu p o treb u : o n daje bro j bajtova
koje podaci zauzim aju. Prenosivost je n ajb itn iji razlog za korišćenje o p e ra to ra sizeof() u
C -u i C ++-U . Različiti tip o v i p o d atak a m o gu im ati različite veličine n a različitim raču -
n arim a. Stoga p ro g ra m e r m o ra da im a in fo rm aciju o veličini tip o v a koje koristi, p re nego
što izvrši neku o peraciju za koju je b itn a veličina p ro m en ljivih. Na p rim er, jed a n ra ču n ar
m ože da čuva celobrojne veličine sa 32 bita, d o k d ru g i to m ože d a rad i sa 16 bitova. Pro-
gram i na p rv o m ra ču n a ru mogli bi da čuvaju veće v red n o sti u c elob ro jn im p ro m en ljiv a-
m a. Kao što m ožete 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 veličine 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 išljajte o prenosivosti - o n a je u g rađ e-
na u jezik.

Sažet pregled operatora


Sledeći 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 ožc prevesti bez grešaka, jer su redovi koji m og u da izazovu grešku 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 logičkog 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;
// Poređenje 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;
// Složena 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;
// Poređenje 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);
// Složena 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 etičk 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;
// Poređenje 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 );
// Složena 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 ;
// Poređenje 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 );
// Složena 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 etič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;
// Poređenje 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;
// Složena 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 etič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;
// Poređenje 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;
// Složena 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 etič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;
// Poređenje 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;
// Složena 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 etič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;
// Poređenje 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;
// Složena 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 ažn ju n a to da je tip boolean p riličn o o graničen. M ožete m u đ o d eliti vred-


n o sti true i false i ispitivati v re d n o st, ali ne m ožete da vršite sab iran je ili bilo koju d ru g u
operaciju.
Kod tip o v a char, byte i short m ožete 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 tičk ih o p erato ra . Svaka aritm etičk 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 vraćen u
p rv o b itn i tip (što je sužavajuća 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 oći će d o p rek o račenja. 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


// Iznenađenje! Ja va dozvoljava prekoračenje.

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 grešci niti u p o zo ren je od prevodioca, niti se javlja izuzetak


pri izvršavanju. Java je d o b ra , ali nije tohko d o b ra.
Složena dodeljivanja nc zahtevaju eksp licitn u konverziju za tipove char, byte ili short,
iako vrše p ro širen ja koja im aju isti rezu ltat kao i d irek tn e aritm etičke operacije. S d ru g e
stran e, izostavljanjem eksplicitne konverzije zasigurno se uprošćava kod.
M ožete 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 raču n a o efektu suža-
vanja kađa vršite e k sp liđ tn u konverziju u m anji tip, jer m ožete n ehotice da izgubite
inform acije tok o m konverzije.
Vežba 14: (3) N apišite 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. Ispišite rezultate. 7.a = = i != obavite i test equals(). Po-
zovite svoju m e to d u iz main() s nekoliko različitih String objekata.

Sažetak
Ako im ate iskustva s bilo kojim jezik o m čija je sintaksa slična C -ovoj, videli ste kako su Ja-
vini o p e rato ri toliko slični o n im a iz vam a zn a n ih jezika da ih gotovo ni ne treb a učiti.
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.
Rešenja odabranih vežbi data su u elektronskom dokumentu Thinking in lava Annotatcd Solution
Guide, koji se može kupiti na lokaciji tvww.MindView.rwt.
Kontrolisanje izvršavanja
Kao i svesno biće, i program tnora da upravlja svojim okruženjem i da bira šta će da radi.
U lavi birate pom oću naredaba za kontrolisanje toka programa.

JAVA KORISTI SVE NAREDBE JEZIKA C ZA KONTROLU IZVRŠAVANJA, STOGA ČE VAM, AKO STE
p ro g ram ira li na C -u ili C ++-U , veći 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
sličnosti. M eđ u rezervisane reči za k o n tro lu izvršavanja 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 ržava p riličn o
o p a sn u n a re d b u goto (koja i dalje m ože b iti najefikasniji način za rešavanje o d re đ e n e vr-
ste p ro b lem a ). I dalje m ožete da n ap rav ite skok koji je nalik na goto, ali je m n o g o više
o g ran ičen o d tip ičn e nared b e goto.

Logičke vrednosti
Sve uslovne n ared b e koriste tačn o st i n etačn o st uslovnih izraza da bi odredile to k izvrša-
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 vraća 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 ože se ko ristiti za fo rm iran je uslovnog izraza. O b ra-
tite p ažn ju na to da Java ne dozvoljava da kao logički tip (boolean) koristite broj, iako je
to dozvoljeno u C -u i C ++-U (gde je tačn o sve različito o d nule, a n etač n o je nu ia). Ako
želite da k o ristite nelogički tip u logičkom 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 način na koji se k o ntroliše to k p ro g ram a. D eo else nije
obavezan, pa stoga if m ožete da koristite u dva oblika:

i f (1ogi č k i- i zraz)
naredba

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

Logički izraz m o ra da vraća rezultat tipa boolean. Naredba je jed n o stav n a nared b a
koja se završava z nakom tačka i zarez, ili složena n ared b a, o d n o sn o g ru p a naredaba u n u -
ta r vitičastih zagrada. Svaki p u t kada se koristi term in naredba, p o d razu m ev a se da ona
m ože biti jednostavna ili složena.
100 Misliti na Javi

Kao p rim e r za if-else, sledi m eto d a test() koja saopštava da li je p retp o stav ljen a vred-
n ost veća, 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 + + , uvlaćenje tela n ared b e za k o n tro lu
izvršavanja je korisn o kako bi čitalac m ogao Iakše da o dredi gde o n o počinje i gde se za-
vršava.

Petlje
Petlje se k o n tro lišu rezervisanim rečim 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 logički-izraz za k o n tro lu ne dobije vred-
n o st false. O blik petlje w h ile je sledeći:

w hile (1o g ič k i-izraz)


naredba

Logički-izraz se izračunava je d n o m na p o četk u petlje i p o n o v o pre svakog koraka iz-


vršavanja naredbe.
Poglavlje 4: Kontrolisanje izvršavanja 101

Sledi je d n o stav an p rim e r koji generiše slučajne 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 tičn u m eto d u random() iz b iblioteke Math, koja generiše
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() vraća 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 eđu while i do-while jeste ta da se n ared b a u petlji do-while uvek iz-
vrši 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-
tačan pre ulaska u petlju vvhile, n ared b a se neće izvršiti n ijed n o m . U praksi, do-while se
rede javlja od vvhile.

Petlja for
Petlja for je verovatno najčešći oblik ponavljanja (ciklusa). O na vrši inicijalizaciju pre pr-
vog izvršavanja ciklusa. Z atim isp itu je uslov ( logički-izraz) i ako je o n isp u n jen , izvršava
narcdbu, a p o to m na kraju svakog ciklusa izvršava korak i p o n o v o ispituje logički-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, logički-izraz ili korak m ože b iti prazan . Logički izraz
se prov erava p re svakog izvršavanja n aredbe. Č im izraz d obije v red n o st false, petlja se
p rek ida i izvršavanje 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 izvršavanja n a red b e), izvršava se korak.
Petlje for se o b ičn 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 ažn 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 očetku m eto d e m a in (). O blast važenja p ro m e n -
ljive c je sam o n a red b a koju k o n tro liše 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 ože da rezerviše p ro sto r
za njih. U Javi i C + + -u definicije p ro m en ljiv ih m ožete da napišete 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.
Vežba 1: ( I) N apišite p ro g ra m koji ispisuje brojeve o d 1 do 100.
Vežba 2: (2) N apišite p ro g ra m koji generiše 25 slučajnih 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 veča, m anja ili jed n ak a drugoj
slučajno generisan o j v red n o sti.
Vežba 3: (1) Izm en ite vežbu 2 tako da n jen kod u klopite u b eskonačnu 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 ičn o pritisk o m na C o n tro l-C ).
Poglavlje 4: Kontrolisanje izvršavanja 103

Vežba 4: (3) N apišite p ro g ram koji p o m o ću dve u gneždene 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 ).
Vežba 5: (4) P onovite vežbu 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 služi 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 izvršavanju koraka k o n tro ln o g izraza, m ožete na-
vesti više n a red ab a razdvojenih zarezim a i o n e će biti izvršene sekvencijalno. P o m o ću
o p e ra to ra zarez m ožete definisati više 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 ože im ati više definicija istog tipa. iM ogućnost da se prom en ljiv e definišu u n u ta r kon-
tro ln o g izraza o g ran ičen a je sam o na petlje for. O vaj p ristu p ne m ožete 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 očite da su, i u delu za inicijalizaciju kao i u d elu koraka, n ared b e izvršavane
u sekvencijalnom p o retk u .

Foreach sintaksa
Java SE5 uvodi novu i sažetiju sintaksu naredbe for koja se u p o trebljava za nizove i kontej-
nere (o kojim a ćete više 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 definiše 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 korišćenje foreach sintakse k a n d id at je svaka m e to d a koja vraća niz. P rim era radi,
klasa String im a m e to d u toCharArray() koja vraća 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 izvršavanja 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 slučaju 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 pojeđn 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 eneriše 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 znači da se uz isto im e m eto d e m o g u zadavati


različite liste a rg u m e n a ta (o p rek lap an ju će u skoro biti reči). Prvi prek lo p ljen i oblik m e-
tode range() počinje o d nule i daje brojeve d o v rh a opsega (engl. range), isključujući sam
v rh . D rugi oblik počinje 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 treći ob lik p rim a k o rak povećanja u svom trećem 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() češće o m ogućava korišćenje foreach sintakse i stoga m ožda
povećava čitljivost koda, ali don ek le sm an ju je efikasnost. Z ato, ako pokušavate da povećate
efikasnost svog prog ram a, izm erite njegove p erfo rm an se profajlerom (engl. profiler).
M ožda 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 služi za v išek ratn o p isanje u istom redu.
106 Misliti na Javi

Foreach sintaksa štedi nešto v rem en a prilik o m pisan ja koda, ali je važnije to što je takav
k od m nogo čitljiviji - kazuje šta hoćete (d o b iti svaki elem en t n iza), a ne kako to postižete
(„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 oguće.

Rezervisana reč return


Za bezuslovno skakattje služi nekoliko rezervisanih reči: return, break i continue, a p o sto -
ji i način skakanja n a n a re d b u sa o zn ak o m (engl. label), slično 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 vraća 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 završava rad m etode.


U koliko u m eto d i koja vraća void nem ate n are d b u return, završetak 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 vraća 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.
Vežba 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, početak i kraj. Za svaku vrednost treb a pro v eriti da li je njen iznos izm eđu
početka i kraja (ukijučivo).
Poglav|je 4: Kontrolisanje izvršavanja 107

Rezervisane reči break i continue


Tok petlje takođ e m ožete da k o n tro lišete p o m o ć u rezervisanih reči break i continue u n u -
ta r tela bilo koje petlje. N aredba break izlazi iz petlje, b ez izvršavanja o stalih n a red ab a
u n u ta r petlje. N aredb a continue zaustavlja izvršavanje teku ćeg ciklusa i v raća se n a p oče-
tak petlje kako b i p očeo n o v korak.
Sledeći 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 reči 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; // Izađi iz p e tlje
i f ( i % 9 != 0) continue; // Sledeći 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; // Izađi iz p e tlje
i f ( i % 9 != 0) continue; // Sledeći 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;
// "beskonačna" p e tlja :
w h ile (tru e ) {
i++;
in t j = i * 27;
i f ( j == 1269) break; // Izađi 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 ođe 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 način o b ičn 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 izvršavanje o d v rh a
petlje (stoga uvećava 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

Posleđnji d eo prik azu je b e sk o n ačn u p etlju while, koja b i se, teorijski, izvršavala 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 etiće te d a n a re d b a continue v raća izvršavanje na v rh petlje i d a ne izvršava
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 ačn e p etlje je for(;;). P revodilac tu m a č i while(true) i for(;;) n a isti
način, p a je izb o r isključivo p ita n je p ro g ram e rsk o g ukusa.
Vežba 7; (1) P repravite vežbu 1 tak o d a p ro g ra m završava ra d p o m o ć u rezervisane reči
break, k ad a v re d n o st d o đ e d o 99. Pokušajte 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 početka. Z aista, goto je
nastao kao posledica k o n tro le p ro g ra m a u m ašin sk o m jeziku: ,A ko je isp u n jen uslov A,
skoči tam o , u su p ro tn o m , skoči o v am o “. K ad p ro čitate asem blerski ko d koji na k raju ge-
neriše p rak tičn o bilo koji p revodilac, p rim etiće te d a se k o n tro la p ro g ram a sastoji o d više
skokova. (P revodilac Jave p rav i sop stveni „asem blerski k o d “, ali njega izvršava v irtueln a
m ašina 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 tačke n a d ru g u , zar ne po stoji način 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 korišćenju
n ared b e g o to, već u n jen o m prečesto m ko rišćen ju - u retkim situ acijam a g o to je najbolji
način za s tru k tu rira n je to k a izvršavanja.
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 nešto što liči na skok, pov ezan o sa rezervisanim rečim a b re a k i c o n tin u e .
To nije skok, već više način da se izađe 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 dvotačka, 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 znači baš nep o-
sred n o - ne valja stavljati bilo koje d ru g e n ared b e izm eđu 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 reči b re a k i c o n tin u e
o b ičn 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 izvršavanja 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 spoljašnjom . U (2), continue


se vraća 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 oljašnju petlju, sve d o oznakel. Z atim se p etlja nastavlja, ali počev o d sp oljnog ciklusa.
U (4), break oznakal tak o đ e vrši 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 "označenom naredbom break" i "označenom 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 ; ) { // beskonačna 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++; // Inače se i nikad
// ne uvećava.
break;
}
i f ( i == 1) {
p r in t("n a s ta v i s p o lja š n ju ");
i++; // Inače se i nikad
// ne uvećava.
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 možete da pozovete
// označene naredbe break i continue
}
}/ * Is p is :
i =0
n astavi unutrašnju
i = 1
nastavi unutrašnju
i =2
nastavi
i =3
preki ni
i =4
nastavi unutrašnju
i = 5
nastavi unutrašnju
i = 6
nastavi unutrašnju
i = 7
nastavi spoljašnju
i =8
p rekini spoljašnju
* ///:-

O b ratite pažn ju na to da n are d b a break p rek id a p etlju for i da se izraz za uvećavanje


ne izvršava sve d o kraja prolaza k ro z p etlju for. Pošto break preskače izraz za uvećavanje,
d o uvećanja dolazi sam o kada je i = = 3. N ared b a continue spoljasnja u slučaju kada je i
= = 7 takođe skače na v rh petlje i preskače uvećavanje, stoga se i o n o vrši direktno.
Da nem a n ared b e breakspoljasnja, ne bi p o sto jao način da se n ap u sti spoljašnja petlja
iz u n u tra šn je petlje, p o što sam a n a re d b a break m ože da p rek in e sam o tre n u tn u petlju.
(Isto važi i za continue.)
Kada n a p u štan je petlje tak o đ e zn ači izlazak iz m eto d e, m ožete koristiti return.
Sledi p rim e r o zn ačen ih n ared ab a break i continue s p etljam a while:

//: control/O znacenW hile.java


// P e t lje vvhile sa "označenom naredbom break" i "označenom 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 izvršavanja 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 :
Spoljašnja p e t l j a while
i = 1
nastavi
i = 2
i = 3
nastavi spo lja šnju
Spoljašnja p e t l j a while
i = 4
i = 5
preki ni
Spoljašnja p e t l j a while
i = 6
i = 7
p re k in i spoljašnju
* ///:-

Ista prav ila važe i za vvhile:


1 . O b ičn a n ared b a continue vraća k o n tro lu n a v rh tekuće p etlje i nastavlja.
2. O značen a nared b a continue vraća 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 značen a nared b a break izlazi iz petlje obeležene zad a to m oznakom .
112 Misliti na Javi

V ažno 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 više nivoa u gnežđivanja.
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 otežavaju analizu p ro g ram a. O b ra tite p ažn 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 ičen 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 slučaj kada neka
osobin a jezikai postaje korisnija kada se ograniče 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 logičku 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
više njih, vitičaste zagrade nisu p o tre b n e) biće izvršena. A ko ne p ro n a đ e ekvivalentnu
v red n o st, biće izvršena naredba označena kao default.
P rim etićete u p reth o d n o j definiciji da se svaki blok case završava sa break, čim e se to k
izvršavanja p rem ešta na kraj tela nared b e switch. O vo je uob ičajen naćin fo rm iran ja na-
redbe switch, ali je break o pcioni. Ako nedostaje, izvršavaće se sledeće case n aredbe sve
d o k se ne naiđe na break. Iako o b ičn o ne želite da se to dešava, isk u sn o m p ro g ram e ru ovaj
p ristu p m ože biti koristan . O b ratite pažnju na to da p o sled n ja nared b a, posle default,
nem a break jer se izvršavanje nastavlja baš tam o gde bi se io n ak o odvijalo nakon break.
M ožete, bez ikakvih posledica, da stavite break na kraj bloka default ako sm atra te da je to
važno zbog stila.
N aredba switch o m ogućuje čistu realizaciju izbora izm eđu više različitih puteva izvrša-
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 logički m o ra te da koristite niz naredaba
if. Na kraju sledećeg poglavlja, videćete da to ograničenje olakšava 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 izvršavanja 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) generiše broj izm eđu 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 veličine koje se kori-
ste pri po ređ en ju .
114 Misliti na Javi

O b ra tite pažn 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 izvršava u više slučajeva. V odite raču 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 slučaja, inače će p ro g ra m proći dalje i p o četi da izvršava n a-
red b e pred v iđ en e za n a re d n i slučaj.
U izrazu:

int c = slucajan.nextlnt(26) + 'a';

m eto d a Random.nextInt() daje slučajan 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 znači d a se 'a' a u to m atsk i p retv ara u tip int da b i se iz-
vršilo 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 vraćen a u tip char ekspli-
c itn o m konverzijom ; inače b i se ispisivali brojevi.
Vežba 8: (2) N apišite n a red b u switch k oja ispisuje p o ru k u za svaki o d slučajeva (case), a
switch stavite u n u ta r petlje for koja isprobava svaki slučaj. Stavite n are d b u break n akon
svakog slučaja i testirajte p ro g ram , a zatim izbacite nared b e break i v idite šta se dešava.
Vežba9: (2) Fibonačijev n iz c ine b rojevi 1 , 1 ,2, 3, 5, 8 ,1 3 , 21, 34 itd., gde je svaki bro j (o d
trećeg nadalje) je d n ak zb iru p re th o d n a dva. N apišite m eto d u koja p rim a ceo bro j k ao ar-
g u m e n t i prikazuje toliko F ibonačijevih brojeva počev 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.
Vežba 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 završnih nula. P rim eri:
1260 = 21 * 60
1827 = 21 * 87
2187 = 27 * 81
N apišite p ro g ra m koji pronalazi sve četvorocifrene v am pirske brojeve. (P redložio D an
F orhan.)

Sažetak
O vim poglavljem se završava proučavanje osnovnih osobina koje se javljaju u većini pro-
gram skih jezika: računanje, p rio ritet operatora, eksplicitna konverzija tipova, uslovi i petlje.
Sada ste sprem ni za korake koji će vas približiti svetu objektno orijentisanog pro g ram iran ja.
N aredno poglavlje će ob rad iti važne tem e: inicijalizaciju i čišćenje objekata te veom a b itn o
sakrivanje realizacije.
R ešenja n ek ih vežbi 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 čišćenje
S napretkom računarske revolucije, ,,nesigurno“ program iranje je postalo jeda n od glavnih
razloga za visoku cenu programiranja.

D V E BITNE OBLASTI SIGURNOSTI SU INICIJALIZACIJA I ČIŠĆENJE. 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 važi 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 . Č išćenje je poseban p ro b lem je r je lako zaboraviti n a elem ent
kada s n jim završite - više vas se ne tiče. Z bog toga se resursi koje taj elem ent ko risti zadr-
žavaju i lako m ožete doći u situaciju d a vam p o n e stan u resursi, i to naročito 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 oslobađa m em o rijsk e resurse kada se n e koriste. U ovom poglavlju
ra z m a tra se inicijalizacija i čišćenje i način n a koji su p o d rž a n i u Javi.

Garantovana inicijalizacija pomoću konstruktora


Z am islite da za svaku klasu koju pišete, n aprav ite m eto d u p o d im en o m inicijalizacija().
Sam o im e nagoveštava da bi ona trebalo da b u d e pozvana pre korišćenja objekta. N ažalost,
to znači da k orisnik m o ra da se seti da pozove tu m etod u. P ro jektant klase u Javi m ože 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 uopšte m ože da m u p ristu p i. Stoga je inicijalizacija zagarantovana.
Sledeći izazov je kako nazvati tu m eto d u . Tu se javljaju dva p ro b lem a. Prvo: bilo koje
im e m ože 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. Rešenje iz C + + -a deluje najlakše i najlogičnije, 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.ogično 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 ažn 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 piše
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 tačno 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 većem
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 ože da im a arg u m e n te koji
om o g ućav 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 ože 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 ićete na sledeći način:

Drvo d = new Drvo(12); / / drvo od 12 metara

Ako je Drvo(int) jed in i k o n stru k to r, prevodilac neće dozvoliti da n ap rav ite objekat
Drvo ni na koji d ru g i način.
Poglavlje 5: Inicijalizacija i čišćenje 117

K on struk tori elim inišu 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
različito o d p o v ra tn e v re d n o sti tip a void, kad a m eto d a ne vraća n išta, ali i dalje im ate m o -
g u ćn o st d a je n ap ra v ite tako da v raća nešto. 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 raća referencu n o v onapravljenog objekta, ali
sam k o n stru k to r ne vraća n išta). 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 .
Vežba 1: (1) N apravite klasu koja sadrži n e in iđ ja liz o v a n u String referencu. Pokažite da
Java tu referencu inicijalizuje v red n o šću null.
Vežba 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 važnih p itan ja u svakom p ro g ram sk o m jeziku jeste u p o tre b a im ena. Praveći
objekat, vi zađajete 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 obraćate se prek o im ena. D o b ro o d a b ra n a im en a čine sistem koji je lakše razu -
m eti i m enjati. To veo m a liči 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 više različitih značenja - 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 reći „operi košulju“, „operi kola“ i
„operi psa“. Bilo bi b u d alasto ako biste m o rali da g ovorite „operiV ešM ašinom k ošulju“,
„operiC rev o m kola“ i „operiK upanjem psa“ sam o da bi slušalac m ogao da n ap rav i razliku
izm eđu tih akcija. Većina Ijudskih jezika je re d u n d a n tn a , pa i dalje m ožete da od red ite
značenje čak i ako ispu stite nekoliko reči. Jedinstveni id en titik a to ri n am n isu p o tre b n i
- značenje m o ž e m o shvatiti iz konteksta.
U većini 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 ožete 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 eće prek lap an je im ena m etoda: kon-
struk to r. Pošto 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 ože
im ati sa m o to je d n o im e. Ali šta ako želite da objek at p rav ite na više načina? N a p rim er,
p re tp o sta v im o da p rav ite klasu koja m ože da se inicijalizuje na sta n d a rd a n način 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 suštinski zn ačajn o u Javi, je r o m o g u ćuje da se isto
im e m eto d e koristi s dru g ačijim 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 opšta 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 običnih 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 čišćenje 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
Sađenje mladice
* ///:-

O bjekat klase Drvo m o že d a b u d e nap rav ljen ili kao m lađica, 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 ogućilo, 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 postojeća visina.
M ože vam zatreb ati d a pozovete m e to d u info() n a više načina. N a p rim er, ako želite da
ispišete 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 više d a kažete, 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 različita
im ena nečem u što očigled n o im a isti kon cep t. N a sreću, p rek lap an je m eto d a om ogućava
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 drugačije 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 otežava održavanje):

/ / : 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 tičn 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 ože au to m atsk i da se p ro širi iz m an jeg u veći, što u k o m b in aciji s prek lap an jem
m ože 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 dešava kada se p ro s t tip
prosleđuje m etodi:

//: inicijalizacija/PreklapanjeProstihTipova.java
// Proširivanje 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 čišćenje 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 etićete 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 slučajevim 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 nešto drugačiji
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 dešava ako je a rg u m e n t veći o d a rg u m e n ta koji se očekuje 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 čišćenje 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 slučaju m etode u zim aju sužene 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 grešku.

Prekiapanje povratnih vrednosti


C esto se čuje pitanje: „Z ašto sam o im ena ldasa i liste arg u m en ata m etoda? Z ašto 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 ože d a razluči značenje iz konteksta,


kao u in t x = f(). M etod u , m e d u tim , m ožete da pozovete i da ignorišete 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 sledeći način:

f0;

kako izvršno o k ru žen je m ože 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 ože 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 izričito 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 definišete 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 neće 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 napišete:

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 kaže: „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 kaže: „N apisali ste k o n stru k to r, znači 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 ežba 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 čišćenje 1 25

Vežba 4: ( 1) P re th o d n o j vežbi 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 vašu p o ru k u .
Vežba 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 različite vrste lajanja,
zavijanja itd. u zavisnosti o d toga koja je p rek lo p ljen a verzija pozvana. N apišite m e to d u
main() koja poziva sve te verzije.
Vežba 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 (različitih tip o v a), ali o b rn u tim redosledom . U verite se da to radi.
Vežba 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 ožete 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 napišete, p revodilac ih ne prih v ata, ali vam daje ide-
ju o to m e šta se dešava.
P retp ostavim o da se nalazite u n u ta r m eto d e i da želite da koristite referencu na tekući
objekat. Pošto 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 ože da
se koristi sam o u n u ta r ne-static m eto d e - vraća referencu na objekat za koji je m etoda po-
zvana. Tu referencu m ožete da koristite kao i svaku d ru g u referencu na objekat. Vodite
126 Misliti na Javi

raču n a o sledećem: 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 . Tekuća referenca this a u to m atsk i se koristi
i za d ru g u m eto d u . Znači, m ožete da napišete:

//: 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 išete 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 izričito 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 tekući 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
* ///:-

Pošto m etoda p re lista j() vraća referencu n a tekući o b jek at preko rezervisane reči th is,
lako se m ože izvršiti više o p eracija n ad istim o b jek to m .
th is se koristi i za prosleđivanje tekućeg 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 pišu this ispred svakog poziva m eto d e i svake reference polja, obra-
zlažući 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 , zbuničete i
naljutiti sve koji če čitati vaš program , pošto u svem ostalom kodu koji su pročitali this nije bio po-
sejan posvuda. Ljudi očekuju da se this upotrebljava sam o o n d a kada je n eo p h o d an . Pridržavajući se
doslednog i jednostavnog stila program iranje štedite vrem e i novac.
Poglavlje 5: Inicijalizacija i čišćenje 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 ProslediPomoćuThis {


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 ože p rim e n iti na
više različitih 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.
Vežba 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


Kađa za klasu napišete 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 više p u ta . Takav poziv m ožete obaviti p o m o ć u
rezervisane reči this.
Kada napišete this, o b ičn o m islite na „ovaj o b jek at" o d n o sn o na „tekući o b jek at“, što
sam o po sebi daje referencu na tekući 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 ačije značenje: 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 ožete sm esta da pozivate:

/ / : in ic ija liz a c ija /C v e t.ja v a


/ / Pozivanje konstruktora pomoću rezervisane reči " 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 = "početni 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 možete da pozovete dva konstruktora!
t h i s . s = s; / / Još jedna upotreba rezervisane reči " 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 može 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 reči this m o -


žete pozv ati jed an k o n stru k to r ali ne i dva. Takođe, k o n stru k to r m o rate da pozovete pre
svih operacija - u su p ro tn o m , biće p rijavljena greška p ri prevođenju.
P re th o d n i p rim e r tak o đ e pokazuje još jed a n način u p o tre b e rezervisane reči 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. Rešenje je da se p o d a t-
ku čla n u o b raćate 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 neće 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 .
Vežba 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 čišćenje 129

Značenje rezervisane reči static


Pošto ste u p o z n a li rezervisanu reč this, m ožete p o tp u n ije da shvatite šta znači k ad a n ek u
m e to d u pro glasite statičn 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 ožete da pozovete n e statičn e2 m e to d e (iako je o b ra tn o m oguće). S tatičn u m e to d u ta-
kođe 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 statičn 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 statičn u m eto d u , o n a m ože 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 išljenje da statičn 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 statičn 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, statičn e m e to d e i polja su
p rag m atičn 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 teoretičarim a.

Čišćenje: finalizacija i sakupljanje smeća


P rogram eri su svesni značaja inicijalizacije, ali često zaboravljaju na važnost čišćenja. 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 ,,napuštanje“ objekta kada s n jim završite nije uvek bezbedno. N aravno,
Java im a sakupljač sm eča koji oslobađa m em o riju objekata koji se više ne koriste. R azm o-
trim o je d a n neobičan slučaj. 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 eča zna sam o kako da oslobodi m e m o riju d o d e-
ljenu pomoću nevv i neće znati kako da oslobodi ,,posebnu“ m em o riju tog objekta. Da bi se
rešio taj slučaj, u Javi je obezbeđena m etoda finalize() koju m ožete da definišete u svojoj
klasi. Evo kako bi o na trcbalo da radi. Kada je sakupljač sm eća 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 eća oslobođiće i m em o riju objekta. Z nači, ako koristite m e to d u finalize(), o n a
vam o m o g u ću je da nešto važno očistite u trenutku sakupljanja smeća.
O v de se krije p o ten cijalan izvor grešaka, jer neki p ro g ra m e ri, p o seb n o o n i vični C + + -
u, u p o četk u m og u da p o m ešaju m eto d u finalize() s destruktorom u C ++-U , što je fu n k -
cija koja se uvek poziva kada se objekat uništava. O vde je važno d a n ap rav ite razliku iz-
m eđ u C + + -a i lave, jer se u C ++-U objekti uvck uništavaju (u p ro g ra m u bez grešaka), d o k
u Javi sakupljač sm eća ne počisti uvek objekte. D ru g im rečim a:
1. M ože se desiti da sakupljač sm eća ne počisti vaš objekat.
2. Saku pljanje sm eća nije uništavanje.
Ako to zap am tite, nećete im ati nevolje. To znači sledeće: ukoliko im ate nešto 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 slično, pa m o rate d a n ap rav ite uob ičajen u m e to d u koja će o b av iti čišćenje. Na

To je m oguće sam o kada toj statičnoj m etodi prosledite referencu na objekat (i statična m etoda m ože
praviti sopstvene objekte). Tada preko te reference (koja je u to m slučaju igra ulogu refe ren ce th is)
m ožete da pozivate nestatične m etode i da pristupate nestatičnim poljim a. Ipak, kada želite nešto
tako da radite, napravite sam o običnu nestatičnn 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 rišete njegovu sliku sa ek ran a, m o žd a o n a n ikad neće 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-
pljača sm eća 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 eća ne reši da
počisti vaš objekat, slika o staje na ekranu.
M ože 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
vašem p ro g ra m u n ik a d a neće p o n e stati m em o rije. A ko se p ro g ra m završi, a sakupljač
sm eća 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 raćen 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 raču n a rsk ih resursa - ako čišćenje nije p o tre b n o p a se p o -
boljšavaju p erfo rm an se p ro g ram a.

Čemu služi 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 čišćenja opšte n am ene, če-
m u , o n d a, o n a služi?
Treća stv ar koju tre b a d a zap am tite glasi:
3. Sakupljanje sm eća se bavi sam o m em o rijo m .
D ru gim rečim a, sakupljač sm eća postoji sam o da bi oslobađao m em o riju koju vaš pro-
gram više ne koristi. Z bog toga svaka aktivnost koja je povezana sa sakupljanjem sm eća, p o-
sebno m etod a finalize(), m o ra takođe da se bavi sam o m em o rijo m i n jenim oslobađanjem .
D a li to znači da m eto d a finalize() treba eksplicitno da oslobađa objekte ukoliko ih
sadrži vaš objekat? Ne, je r o oslo b ađ an ju m em orije objekata vodi računa sakupljač sm eća,
i to bez o bzira na to kako su objekti napravljeni. M etoda finalize() je korisna sam o u po-
sebnim slučajevim a, kada vaš objekat zauzim a m em o riju na neki dru g i način, ne pravlje-
njem objekata. Ali, kao što znate, u Javi je sve objekat, pa kako je, onda, taj slučaj 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
koristeći neki m eh an izam sličan C -ovom , um esto na u o bičajen način. To se prvenstveno
m ože desiti p ri p ozivu lokalnih m etoda (engl. native m ethods) koje služe 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 ožete da pozovete p o tp ro -
g ram na n eko m d ru g o m jeziku, zapravo m ožete 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 neće biti o sloboden i javiće 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 vaše m eto d e finalize().
Pošto ste sve ovo pročitali, v ero v atn o vam je jasno d a nećete č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 uobičajeno čišćenje.
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 korišćenje završnih 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 čišćenje 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 čiščenje u p o g o d n o m tre n u -
tku . To zvuči p riličn 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 uništav aju , to jest, svi objekti b i trebalo da b u d u u n išten 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 ištav a se k ada se n a iđ e n a zatv o ren u v itičastu zag radu koja završava oblast važenja
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 neće biti n ik ad a pozv an i nastaće „curenje
m e m o rije “ a p o red toga ni d ru g i delovi o b jekta neće biti počišćeni. O va v rsta grešaka
o tk riv a se vrlo teško 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 eća to rad i u m esto vas. P ojednostavljeno gledano, m o -
žete reći d a Java n e m a d e stru k to re zato što im a sakupljače sm eća. Kako ova knjiga o d m i-
če, videćete d a p o stojan je sakupljača sm eća ne elim iniše p o tre b u za d estru k to rim a
i n jih o v im korišćenjem . (A nikada n e treb a d irek tn o da pozivate finalize(), p a to, dakle,
nije rešenje 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 čišćenje,
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 eća n iti čišćenje. A ko JV M -u ne p o n e-
stane m em o rije, on (m u d ro ) neće gub iti v rem e na o slo b ađ anje m em o rije sakupljanjem
sm eća.

Stanje okončanja
U glavnom ne m ožete da se oslonite na to da će finalize() biti pozvana, već m o ra te da na-
p rav ite zasebne funkcije za čišćenje 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 ičajen način, koju
većina p ro g ra m e ra nikada neće 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 okončanja' (engl. ten n in a tio n condition) nekog objekta.
O d tre n u tk a kada vas objekat više ne zan im a - kada je sp rem an da b u d e počišćen - taj
o bjekat treba da b u d e u stan ju u kom e njegova m em o rija m ože 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 eća počisti taj objekat. A ko bilo koji deo objekta
nije p ra v iln o počišćen, im aćete grešku u p ro g ram u koju je vrlo teško o tk riti. M etoda fi-
n a lize() je značajna zato što m ože da se iskoristi za otk riv an je tak vih slučajeva i ako se ne
poziva uvek. Ako neka finalizacija o tk rije grešku, 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 našeg 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 počišćen
/ / pomoću 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 (''Greška: k njig a n i j e vraćena");
/ / Normalno b is te u r a d i l i i sledeće:
/ / 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 smeća i f i n a l i z a c i j u
System.gc();
}
} / * Is p is :
Greška: Knjiga n i j e vraćena
* ///:-

Sve pozajm ljene knjige u stan ju o k o n ćan ja treb alo bi da b u d u v raćen e pre nego što ili
pok u pi sakupljač sm eća, ali se do g o d i da su m eto d i m a in () p ro g ra m e r greškom 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 grešku koju je p o ten cijaln o teško o tkriti.
O b ra tite p ažn ju na to da je za zahtevanje čišćenja korišćen 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 izvrša-
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 eća).
Po prav ilu , treb alo bi da p retp o stav ite kako i verzija fin alize() u osnovnoj klasi irna
važnog posla i da je pozovete p o m o ć u rezervisane reči su p e r, kao što vidite u K njiga.fi-
nalize(). U ovom slučaju, 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 čišćenje 1 33

Vežba 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 aša n je svog p ro g ra m a.
Vežba 11: (4) Prepravite p re th o d n u vežbu tak o d a se vaša m e to d a finalize() uvek poziva.
Vežba 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 okončanja da m o ra b iti p ra z n a p rilik o m čišćenja objekta. N apišite 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č smeća?


Ako ste prešli s p ro g ram sk o g jezika u k o m e je za pravljenje o b jek ta u din am ičk 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 ičk o j m em o riji ta k o đ e zahtevan. M e đ u tim ,
ispostavlja se da sakupljanje sm eća m ože značajn o u ticati n a povećanje b rzin e p ravljenja
objekata. To u po č etk u zvuči p o m alo č u d n o - o slo b ađ an je p ro s to ra u tiče n a dodeljivanje
p ro sto ra - ali na taj način rad e neke v irtu eln e m a šin e za Javu i to znači da dodeljivanje
p ro sto ra u dinam ičk 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 ožete 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 ože kasnije d a b u d e n a p u šten i za-
tim p o n o v o korišćen. U n ek im Javinim v irtu e ln im m ašin am a, d in am ič k a m em o rija
m ože da b u de p o tp u n o drugačija; više 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 znači d a je d o d eljivanje p ro s to ra veo m a brzo. „D ina-
m ički 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 tražen ju m esta za ostavu.)
O b ra tite pažn ju na to da d in am ičk a m em o rija nije p o k re tn a trak a i ako je na taj naćin
tretirate, ub rzo ćete doći u situ aciju da vam se p ro g ra m raširi n a više m em o rijsk ih stra-
nica - koje se snim aju na đisk i vraćaju s njega u b rzu m e m o riju , pa će izgleđati da im ate
više m em o rije nego što je zapravo slućaj. P ro m en a stran ice zn ačajno sm anjuje p erfor-
m anse. Na kraju, n akon što n ap rav ite dovoljan broj o b jekata, p o nestaće vam m em o rije.
Tu na scenu stu p a sakupljać sm eća - d o k radi, o n isto v rem en o sabija sve objekte u dina-
m ičkoj m em o riji čim e se „dinam ički pokazivač“ p o m e ra bliže p o četk u p o k re tn e trak e i
dalje o d kraja stranice. Sakupljač sm eća reo rg an izu je stvari i om og ućav a da se za d odelji-
vanje p ro sto ra koristi m odel brze d in am ičk e m e m o rije b esk on ačn e dužin e.
Da biste shvatili kako to radi, treba da m alo bolje razu m ete kako rad e sakupljači sm eća
(engl. garbage collector, gc). Pri njihovom rad u koristi se jednostavna ali spora tehnika - bro-
janje referenci. To znači da svaki objekat im a brojač referenci i d a se taj brojač uveća za jedan
svaki p u t kada se objektu prid ru ži neka referenca. Kad god referenca izađe iz oblasti važenja
ili bude postavljena na vredn o st n u ll, brojač referenci se um anji za jedan. P rem a tom e, vo-
đenje računa o brojaču referenci je m ali, ali stalan režijski trošak koji se obavlja za vrem e
trajanja vašeg program a. Sakupljač sm eća proiazi kroz celo k u pn u listu ob jekata i oslobađa
m em o riju kada naiđe na objekat čiji je brojač referenci nula. (M eđ utim , u realizacijam a
13 4 Misliti na Javi

brojača referenci objekat se često oslobađa čim brojač p ad n e n a nu lu .) Jeđna o d m an a ovog


p ristu p a je sledeća: ako su objekti uzajam n o povezani referencam a, njihovi brojači referenci
m o g u biti različiti od nule, čak i ako ti objekti predstavljaju sm eće. O tkrivanje takvih sam o-
referencirajućih grupa značajan je d o d atn i posao za sakupljač sm eća. Brojanje referenci se
često koristi da objasni jed n u vrstu sakupljanja sm eća, ali se n e p rim en ju je gotovo n i u jed-
noj stvarnoj virtuelnoj m ašini.
U b ržim realizacijam a, sakupljanje sm eća 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 ožem o n a k raju p ro n aći n e k u refe-
rencu koja se nalazi bilo na steku ili u statičn o m skladištu. Lanac veza m ože ići kroz neko-
liko nivoa objekata. Znači, ako krenete o d steka i statičn o g skladišta, i p ro đ ete kroz sve
reference koje se tam o nalaze, p ro n aći ć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 završava kada pro đ ete kroz celu M režu
koja je započela to m referencom n a steku ili statičn o m skladištu. Svaki objekat kroz koji
p ro đ ete sigurno je živ. O b ratite p ažn ju n a to da n e m a p ro b lem a sa izdvojenim sam orefe-
rencirajućim grupam a, jer njih na ovaj način ne nalazite i stoga au tom atski postaju smeće.
U gore o p isa n o m p ristu p u , JVM k o risti prilagodljiv (engl. adaptive) n ačin sakupljanja
sm eća. Š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 znači 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 ičk 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 eće. Pored toga, o bjekti se pri k o p ira n ju u n o v i di-
nam ičk i m em orijski p ro sto r slažu 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 skladišni 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 tiče iz d in am ičk o g m e m o rij-
skog p ro sto ra ili statičkog skladišta m ože 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 naići 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 sakupljače 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 , trošeći d v o stru k o više m em o rije nego
što vam zaista treba. Neke v irtu e ln e m ašine ovo rešavaju tako što zau zim aju d in am ičk i
m em o rijsk i p ro sto r u k o m ad ićim a, p o p o treb i, i k o p iraju iz je d n o g k o m ad ića u drugi.
D rugo pitanje je sam proces k o piranja. Kada vaš p ro g ram p o stan e stabilan, praviće
m alo ili n im alo sm eća. 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 sprečile, neke v irtu eln e m ašine
otkrivaju kada se više ne pravi sm eće i prelaze na drugačije čišćenje (ovo je ,,prilagodljiv“
deo). D rugi p ristu p se naziva označi-i-počisti (engl. rnark a n d sweep) i njega su starije vir-
tuelne m ašine kom panije Sun koristile sve vrem e. Za o p štu u p o tre b u , teh n ik a ozn ači-i-p o -
čisti je priličn 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 označi-i-počisti k oristi se ista logika polaženja o d steka i statičkog skladišta
i p raćen ja svih referenci da bi se p ronašli živi objekti. Svaki p u t kada se naiđe na živi obje-
kat, o n se označi in d ik ato ro m , ali se objekat još uvek ne sakuplja. Č išćenje se obavlja tek
Poglavlje 5: Inicijalizacija i čišćenje 1 35

kada se završi p o stu p ak označavanja. T okom čiščenja o slo b ađa se m em o rija koju su zau-
zim ali m rtv i objekti. M eđ u tim , n išta se ne k o p ira, p a ako sakupljač odluči d a sabije frag-
m e n tira n i d in am ički m em orijski prostor, o n to rad i tako što p o m e ra objekte.
Tehnika stani-i-kop iraj počiva na ideji da ovaj tip saku pljan ja sm eća 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 eća. U literatu ri koj u je objavio
Sun p ro n aći ćete više referenci na sakupljač sm eća 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 eća nije realizo-
vano n a taj način. U m esto toga, S unov sakupljač sm eća 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 ači-i—p 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 ašin 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 izvorišnog d in am ičk 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 eća to k o m sakupljanja o bično m ože d a k o p ira objekte u m rtve blokove. Svaki
blo k im a brojačproizvodnje koji vodi evidenciju d a li je b lo k živ. O b ič n o se sabijanje vrši
sam o n ad b lokovim a koji su n apravljeni posle posled n jeg sak up ljan ja sm eća; svim osta-
lim blok ov im a b rojač p roizvodnje se uvećava ako su o d n e k u d referencirani. N a ovaj n a-
čin se b rin e o uobičajenim slučajevim a p riv re m e n ih o b jekata k ratk o g životnog veka.
P erio dičn o se radi p o tp u n o čišćenje - veliki o bjekti se i dalje ne k o p iraju (već im se sam o
uveća brojač proizvodnje), a blokovi koji sadrže m ale o b jek te k o p iraju se i slažu. JVM
p rati efikasnost sakupljača sm eća i ako se za njegovo korišćenje n e o p h o d n o troši vrem e
jer su svi objekti dugog životnog veka, preb acuje ga u režim o zn ači-i-p očisti. Slično tom e,
JVM vodi raču n a o u spešnosti tehnike o zn ači-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 režim 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 označi-i-počisti.
Postoji više d o d atn ih m o g u ćn o sti za ub rzav an je v irtu e ln e m ašine. Posebno važna m o -
gućnost o đn o si se na rad p ro g ram a za učitavanje (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 ičn o ili p o tp u n o
pretvara p ro g ram u lokalni m ašinski kod, tako da JVM ne m o ra da ga in terp re tira, pa je
izvršavanje m n o g o brže. Kada klasa m o ra da se u čita (o b ično , prvi p u t kada želite da na-
pravite objekat te klase), pronalazi se d atoteka .class i b ajtk od te klase se učitava u m em o -
riju. U to m tre n u tk u jedan p ristu p m ože 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, nešto više traje (što, kada se n ak u p i kroz životni vek p ro g ram a , ne
m ože da b u d e zanem arljivo). D rugo, povećava se izvršni 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 znači da se II I prevođenje ne radi sve d o k nije n e o p h o d n o . T im e se postiže da kod koji
se nikad a ne izvrši, m ožda 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 slično, jer o p tim iz u ju svaki deo koda sva-
ki p u t kada se on izvršava - što se kod više izvršava, p o staje sve brži.
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-
rišćenja. 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
inače prijavljuje grešk u ). Z nači, ako napišete:

void f() {
int i ;
i++; // Greška -- i nije inicijalizovana

dob ićete p o ru k u o grešci 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 greška 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 , povećava se v ero v atn o ća o tk riv a n ja grešaka.
A ko je p ro st tip p o lje neke klase, stvari su nešto drugačije. Kao što ste viđeli 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 čišćenje 137

iv.ispislnicijalneVrednosti();
/* U ovom slučaju takođe možete 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 dešava ako želite da p rom enljivoj d odelite p o č e tn u vrednost? D irek tan n ačin 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 definišete u klasi. (To
nije m o guće u C + + -u , iako početn ici u jeziku C + + to uvek pokušavaju.) U sledećem 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 način . A ko je definisan a klasa D u b in a , definišite


p ro m en ljiv u i in iđ ja liz u jte je n a sledeći način:

/ / : 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-
bićete grešku p ri izvršavanju koja se u Javi naziva izuzetak (engl. exception), što je o b ra-
đ eno u poglavlju O brada grešaka potnoću izuzetaka.
M ožete č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 ožete 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 ožete 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 leži u redosledu inicijalizacije (p ro m en ljiv a i se koristi pre nego što je definisana),
a ne u način 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 graničen 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 više fleksibilnosti.
Poglavlje 5: Inicljalizacija i čišćenje 1 39

Inic/jalizacija konstruktorima
K o n stru k to r m ože d a se k oristi za inicijalizaciju. To daje veću fleksibilnost u p ro g ra m ira -
n ju , je r m o žete da pozivate m e to d e i izvršavate 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. Jeđnu stvar treb a im ati na u m u : tim e ne sprečavate 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 važi 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 ušav 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, videćete 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 završeno 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 eće.) Isprva
ovo m ože 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 statičnih elemenata


Za statične podatke postoji jeđ in stv en o skladište, bez o b zira na to koliko je o bjekata na-
pravljeno. R ezervisanu reč sta tic ne m ožete 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 statičn 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 izvršite na m estu definicije, to će izgledati isto kao i kod ne-
statičnih podataka.
Evo p rim e ra iz kojega ćete videti u kom tre n u tk u se inicijalizuje sta tič n o skladište:

/ / : in ic ija l iz a c ija /S ta tic k a ln ic ija liz a c ija .ja v a


/ / Zadavanje početnih 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 čišćenje 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 ogućava da p ra tite pravljenje objekata, a klase Sto i Kredenac na više
m esta u n u ta r definicija im aju statičn e članove klase Cinija. O b ra tite p a ž n ju na to da Kre-
denac pre statičnih definicija p rav i nestatičan 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 statičn 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, statičn i objekti Cinija cinijal i cinija2 nik ad a neće
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 statičn o m p o d a tk u ). N akon toga, više se ne obavlja p o n o v n a inicijali-
zacija statičn ih objekata.
Prvo se inicijalizuju statičn i elem en ti, ako to već nije u čin jen o , a zatim nestatičn 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 izvršila statičn a m e to d a m ain(
), m o ra b iti učitan a klasa Staticlnitialization, a zatim se inicijalizuju n jen a statičn a polja
sto i kredenac, što p ro u zro k u je učitavanje tih klasa; p o što o b e te klase sadrže statičn e o b -
jekte Cinija, zatim se učitava Cinija. Tako se sve klase ovog p ro g ra m a učitavaju p re počet-
ka izvršavanja m etode main( ). To najčešće nije slučaj, p o što u tip ič n o m p ro g ram u sve
neće b iti povezano statičn im e lem en tim a kao u ovom p rim eru .
Da b ism o napravili sažetak 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
statičn 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 statičkoj m eto d i ili statičk 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 pretraživanjem p u ta n je klasa (engl. classpath).
2 . Pri učitavanju P as.class (pri čem u se pravi objekat klase C lass, o čem u će kasnije
biti reči) pokreće se inicijalizacija svih statičn ih elem enata. Z naći, o n i se inicijalizu-
ju sam o jed an p u t, kada se odgovarajući objekat klase C lass učitava p o prvi put.
3. Kada napravite objekat o p eracijo m n ew Pas(), proces kon stru k cije objekta nalaže
da se prvo zauzm e do v o ljn o skladišnog 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. Izvršavaju se sve inicijalizacije koje se javljaju na m estu definicije polja.
6. Izvršavaju se k o n stru k to ri. Kao što ćete videti u poglavlju Ponovno korišćenjc klasa,
to m ože da b u d e veliki posao, n aro čito p ri nasledivanju.
Poglavlje 5: Inidjalizacija i čišćenje 143

Eksplicitna inicijalizacija statičnih elemenata


Java om o gućava da statičke inicijalizacije u n u ta r klase grup išete p o seb n im „statičkim od -
red bam a" (koje se p o n ek ad nazivaju i statički 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 podseća 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, izvršava 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)
* ///:-

Statični 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 ačen 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, statičn i elem en ti neće b iti inici-
jalizovani, kao što v id ite iz rezu ltata p ro g ra m a . Takođe 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 ačen a sa (2); statičn i elem en ati če se
inicijalizovati sam o je d n o m .
V ežba 13: (1) P roverite n are d b e iz p re th o d n o g pasusa.
V ežba 14: (1) N ap rav ite klasu s je d n im sta tičn 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 statičan blok. D ođajte statičn 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 nestatičnih instanci


Z a inicijalizaciju n estatičn ih p ro m en ljiv ih svakog objekta, Java obezbeđuje sličnu 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 čišćenje 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 ( ) završene");
new S o l j e ( l ) ;
p r in t ( "n o v e S o l j e ( l ) završene");
}
} / * 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 ( ) završene
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 ) završene
* ///:-

O b ra tite p ažn 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 statičn 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 ršk a za
inicijalizaciju anortim nih unutrašnjih klasa (poglavlje U nutrašnje klase), ali o m o g u ću je i
da jam čite kako če se o d ređ en e op eracije izvršiti 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
izvršava p re svih k o n stru k to ra.
Vežba 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 definišu i koriste p o m o ć u ope-
ratora indeksiranja [ ]. R eferencu niza definišete 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, značenje će b iti p o tp u n o isto.

in t a l [ ] ;

O vo se uklap a u očekivanja C i C + + p ro g ra m era. Prvi stil sadrži 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 biće ko-
rišćen taj stil.
Prevodilac n e dozvoljava d a navedete veličinu niza. To nas vraća 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 eštanje sam og niza nije rezervisan u m em oriji. D a biste
napravili skladište za niz, m o rate da napišete izraz za inicijalizaciju. Kod nizova, inicijali-
zacija m ože 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 ože 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 vitičastih zagrada. U ovom slučaju, prevodilac
vodi raču n a o zau zim an ju skladišnog 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 služe reference n a nizove bez d o d eljen ih nizova?

in t [] a2;

Pošto je u Javi m oguće da jed an niz d o d elite d ru g o m , m o žete i da napišete:

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 čišćenje 1 47

O b ra tite p ažn 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 slučaju, o n a pokazuje n a d ru g i niz.
Pošto 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 raža-
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 ožete č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 . Pošto u Javi, kao u C -u i C + + -u , n izovi p o č in ju o d indeksa 0, najveći elem en t koji
m ožete da indelcsirate je l e n g t h - 1. A ko izađete van granica, C i C + + to ćutke p rih v ataju
i dozvoljavaju da vršljate dalje p o m em o riji, što pred stav lja izvor m n o g ih grešaka. Java
vas, m e đ u tim , štiti o d takvih p ro b lem a tak o što pro izv o d i grešku p ri izvršavanju ( izu ze-
tak) ako zakoračite 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 pišete 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 ože 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]
} ///■■-

Pošto se velićina 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 slučajan 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
izvršavanja. 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 eričk 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 ože 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 isključite,


š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 išete 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 ršav 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 slučaju 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 ožete, 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 ićem o sam o niz referenci pa inicijalizacija neće biti p o tp u n a sve do k ne inicijalizujem o


sam e reference praveći nove objekte tipa Integer (u ovom slučaju, 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 izvršavanja zab o rav ite da n ap rav ite objekat, javiće 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 koristeći listu izm eđu vitičastih 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 čišćenje 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 slučaja, završni zarez u listi inicijalizatora nije obavezan. (Z ato je lakše o d rža-
vanje dugačkih 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 lakšem o d ržavan ju d ugačkih lista.)
lako je p rv i oblik k oristan , njegova o g ran ičen ja su veća, je r se m ože u p o treb ljav ati
sam o na m estu definicije niza. D rugi i treći oblik m ožete 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 ačin 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 ožete zadati čak i u tre n u tk u to g poziva.
Vežba 16: (1) N apravite niz objekata tipa String i svakom elem e n tu d o d elite p o jed a n
String. Ispišite niz petljom for.
150 Misliti na Javi

Vežba 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 ispišite 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 .
Vežba 18: (2) D o p u n ite p re th o d n u vežbu 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. Pošto sve klase u o sn o v i n asleđ u ju zajedničku k o ren sk u klasu Object (o
čem u ćete više 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 sledeći način:

/ / : in ic ija liz a c ija /P ro m e n ljiv a L is ta .ja v a


/ / Korišćenje 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 ožete 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 ašan je (ako za svoju klasu ne definišete 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 čišćenje 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 definišu p o m o ć u o p era to ra tri tačke, kao što vidite u sledećoj 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


/ / Korišćenje 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) {
/ / Može p r i m i t i pojedinačne 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, više 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 ože foreach sintak som da p ro d e kroza
sve e lem en te niza. M ed utim , na delu je više od p roste a u to m atsk e konverzije liste ele-
m en ata u niz. O b ra tite pažn 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 rosleđuje 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 ožete 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 ože prosled iti
nula arg u m en a ta. To je korisno kada im ate neobavezne prateće 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 OpcioniPratećiArgumenti {


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 ožete 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 sledećem
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 veličina to g niza
nula ukoliko ta lista n e m a sadržaja:

//: 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: Inicijalizađja i čišćenje 153

M etoda getClass() je deo Objecta i biće 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 ispišete, d o b ijate k o d ira n znakovni niz koji predstavlja
tip te klase. Vodeće [ p o k a zu ju d a se rad i o n izu tip a koji sledi. I n azn aču 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 ožete m ešati 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 ( ) ; / / Neće se prevesti - - višeznačno
}
} / * Is p is :
prvi a b c
drugi 1
drugi 2 1
drugi 0
treći
* ///:-

U svakom o d p re th o d n ih slučajeva, 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
greška 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 rešite 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} (Neće 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-


vođenje p rim era iz ove knjige. Ako je prevedete ru č n o , d o b ićete p o ru k u o grešci:
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 čišćenje 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, upaliće:

//: 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.
Vežba 19: (2) N apišite 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 ožete proslediti listu elem en ata tip a String razdvojenih zarezim a ili
String[].
Vežba 20: ( 1) N apišite m e to d u main() koja u m esto u o b ičajen e sintakse u p o treb ljav a p ro -
menljive a rg u m en te. Ispišite sve elem en te rezu ltu ju ćeg niza args. Ispitajte tu m e to d u p o -
m oću različitog 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 olakšava život
kada tre b a da g ru pišete 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 rizičnije i teže upo trebljavaju. Inače, 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 više 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 oćniji 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. Pošto 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 pišem o velikim slovim a (ako im e sadrži
više reči, 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 ažete 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 statičn 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 ašan je
p revodioca to k o m generisanja klase za enum, pa m ožete 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 čišćenje 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 nezačinje 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(Začinjenost.SREDNJE),
fefero n = new P1jeska(Začinjenost.MNOGO);
bez.opiši ( ) ;
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 nezačinjena.
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 .
* ///:-

Pošto sw itc h služi za izbo r iz ograničer.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 pažn 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 opšte uzev, enum m ožete da koristite kao d ru g i naćin đefinisanja tipova p o dataka .
To je i b io cilj, pa na njih više ne m o rate o b raćati p o seb n u pažnju. Pre uvođenja n ab ro ja-
nih tip o v a u Javu SE5, trebalo je uložiti 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 korišćenje 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.
Vežba 21: (1) D efinišite enum o d šest n ajm an je v red n ih tipova p ap irn ih novčanica. M e-
to d o m values() p ro d ite kroz sve n ab ro jan e v red n o sti, ispišite svaku v red n o st i (m e to d o m
ordinai()) njen redni broj.
Vežba 22: (2) N apišite naredba switch za enum iz p re th o d n o g p rim era. Za svaki case,
ispišite op is te novčanice.
158 Misliti na Javi

Sažetak
P rilično razrađ en m e h a n iz am iniđ jalizacije, kakav je k o n stru k to r, treb alo bi da ukaže na
o g ro m n u važnost koja je u o v o m jeziku d ata inicijalizaciji. Jedno od p rv ih zapažanja 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 značajan deo pro b lem a p ri p ro g ra-
m iran ju . O v u v rstu grešaka je teško p ro n ači, a slični p ro b lem i javljaju se i p ri nepravil-
n o m čiščenju. Pošto k o n stru k to ri o m o g u ću ju da garantujete prav iln u inicijalizaciju
(prevodilac neće 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 ištav 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 išten i. Sakupljač sm eća u Javi au to m atsk i oslobađa m em o riju ,
tak o da u većini slučajeva o d g o varaju će m eto d e za čišćenje 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 eća 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 sakupljači sm eća m o g u čak da čiste i drug e
resurse, kao što su id en tifik ato ri d ato tek a i grafičkih objekata. M eđ u tim , sakupljač sm eća
dod aje režijske troškove to k o m izvršavanja p ro g ram a, čiju je cenu teško 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-
boljšaie 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. Pošto ga-
rancija ko n stru k cije važi i kada p rav ite nove klase kom pozicijom ili nasleđivanjem , p o treb-
n a je d o d a tn a sin tak sa da bi se to podržalo. O kom poziciji, nasleđivanju i to m e kako te
operacije u tiču n a k o n stru k to re , naučićete u n ared n im poglavljim a.
R ešenja o d a b ra n ih vežbi 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 ači „m oći 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 NIŠTA NIJE D O BRO
n ap isan o d o k se više 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 učin io čitljiviji i razum ljiviji, a sam im tim će se i lakše o d ržav ati.1
M e đ u tim , posto je i p ro b lem i zbog n asto jan ja d a se p ostojeći k o d m en ja i poboljšava.
Č esto korisnici (p ro g ra m eri klijen ti) raču 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 važno 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 neće m o rati p o n o v o d a p išu 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 neće 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 složi kako neće izbacivati postojeće 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 složenija. A ko je reč o polju,
kako tv orac biblioteke m ože da zna kojim su p oljim a p ristu p ali p ro g ra m e ri klijenti? Isto
važi 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 napiše novu? M enjanje bilo koje od gore p o m e n u tih članica m oglo bi d a n aruši
kođ p ro g ra m e ra klijenta. Stoga su tvorcu biblioteke vezane ruke i ne m ože ništa d a m enja.
Da bi se rešio ovaj p ro b lem , u Javi postoje specifikatori p ristu p a koji tv o rcu biblioteke
o m o g ućava ju da naznači š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 ožete p o m isliti da kao p ro jek tan t b iblioteke treb a sve da čuvate što ,,privatnije“ m oguće
i da izložite sam o m etode koje želite da p ro g ram eri klijenti koriste. To je sasvim tačno,
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 ograničenja. D o kraja ovog poglavlja
trebalo bi d a se uverite u značaj 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 savršeno d o b ar i da prerađivati ga znači gubiti vrem e. Takav argum ent nije
vaijan zato što lavovski deo vrem ena i novca koji se uloži u projekat ne o tp ad a na prvo pisanje koda,
nego na njegovo održavanje. U činiti kod razum ljivijim , znači uštedeti 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 završen. 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 reči package, a n a specifikatore p ristu p a utiče 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 naučićete kako se ko m p o -
n e n te biblioteke stavljaju u pakete, a n ak o n toga m oći ćete da raz u m e te p o tp u n o značenje
specifikatora p ristu p a .

Paket: bibliotečka jedinica


Paket sadrži 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 ožete 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 ožete 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 uvoženje 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 vaših članica klasa su izolovana jedn a od dru gih.
M eto d a f() iz klase A neće 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 veći 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 ože rad iti, i u
nastavku knjige takav p ristu p biće korišćen gde g o d je m oguće, 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 rečav 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 prevođenje (engl. compilation unit, translation unit ). Im e svake jed in ice za p revođenje
m ora da se završava sa .java, a u n u ta r nje m ože 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 ože d a se nalazi sam o jedna jav n a klasa, in ače 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 ršk 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 ožete da dobijete p ri-
ličan broj datotek a .class. Ako ste p ro g ram ira li n a jeziku koji se p rev o d i u izvršni oblik,
verovatno ste navikli da prevodilac p rav i m eđ u o b lik (o b ičn 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 povezivača (engl. linker) d a biste na-
pravili izvršnu 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 način . 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 ičn 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 ačite 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 napišete:

package p r is tu p ;

naznačavate 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, drugačije rečeno, naznačavate da je jav n o im e klase, iz te jed in ice za prev o đ en je, p o d
kišo 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 objašnjeno. (Im ajte u v id u d a konvencija za d avanje im en a Javinim p ak etim a na-
laže da se koriste sam o m ala slova, čak i za reči 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 ršn 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 znači da u njoj m ože
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. Takođe m ože da u p o tre b i p o tp u n o o d re đ e n o im e:

/ / : pristup/OdređenaMojaKlasa.java

p u b lic class OdređenaMojaKlasa {


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/OdređenaMojaKlasa.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 reči p ack ag e i im p o rt o m o -


gućavaju da p odeli jed an g lobalni im enski p ro sto r i izbegne višesm islenost im en a, bez
o bzira na to koliko se Ijudi poveže na In t''rn e t i p o čn e da piše klase n a Javi.

Pravljenje jedinstvenih imena paketa


B uduči 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 ože da
se sastoji o d više d ato tek a .class i stvari m o g u da p o sta n u p rilič n o kom plikovane. Logičan
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 način na koji Java rešava p o m e n u ti p ro b lem ; dru g i način ć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 rešava i jo š dva p ro b lem a:
pravljenje jedinstv enih im en a paketa i pro n alažen 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 ostiže 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. Pošto 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 vašeg pak eta će sig u rn o b iti jed in stv en o i neće doći
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 piše 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 vaše 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 odlučili 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 ličnog 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 ašem ra-
ču n a ru . Kada izvršni sistem treb a da u čita d a to te k u .class, m ože 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 sledeće: 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 sadrži je d a n ili više di-
rek to riju m a koji se koriste kao korenski za tražen je d ato tek a .class. In te rp re ta to r u im en u
paketa zam enjuje svaku tačku 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 nešto drugo, u zavisnosti od vašeg 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 traži d ato -
teku .class čije im e odgovara klasi koju p okušavate d a napravite. (O n tak o đ e p retražu 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 ispišem 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 piše m alim slovim a.) Ako
od lućim da n ap rav im biblioteku p o d im en o m simple, u n eću 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 ože da se koristi kao im enski p ro s to r za sledeće 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 rečeno, 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 slična:

//: 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 eštaju 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 sledeći sadržaj:

CLASSPATH=. ; D: \JAVA\LIB; C: \D0C\JavaT

V idite da p ro m en ljiv a CLASSPATH m ože da sađrži više a ltern ativ n ih p u ta n ja za pre-


traživanje.
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 nači, ako bi se JAR dato tek a zva-
la grozd.jar, u vašoj p ro m en ljiv o j CLASSPATH treb alo bi da piše:

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 ožete 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 počinje da p retra-


£uje d ire k to riju m e n av ed en e u prom enljiv o j CLASSPATH, tražeći 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 podešavanje p rom enljive CLASSPATH bio je veliki p ro b lem početnicim a u Javi
(meni takođe, kada sam počinjao), p a je SU N u kasnijim verzijam a Jave„opam etio“ razvoj-
no okruženje. Kada instalirate Javu, prim etićete da ćete m oći da prevodite i pokrećete
osnovne p ro g ram e u Javi, čak i ako n e podesite prom enljivu CLASSPATH. M eđutim , da bi-
ste preveli i izvršavali 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 raćete d od ati osnovni direkto riju m stabla koda laijige.
Vežba 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 dešava ako se p rek o * uvezu dve biblioteke koje sadrže ista im ena? N a p rim er, p ret-
postavim o da u p ro g ra m u piše:

import ne t.m in dview.sim ple .*;


import j a v a . u t i l . * ;

Pošto i java.util.* tak o đ e sadrži klasu Vector, to m ože d a izazove sukob im ena. M eđu-
tim , dokle g od ne napišete k o d koji izaziva d vosm islenost, sve će b iti u red u —to je do b ro ,
inače biste m o rali m n o g o da pišete d a biste izbegli sukob koji se nik ad a neće 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 ože da zna, kao ni čitalac. Stoga
se prevodilac žali i zahteva da b u d ete izričiti. Ako, na p rim er, želite sta n d a rd n i Javin Vec-
tor, m o ra te da napišete:

j a va .u ti1 .Vector v = new ja va .u ti l. Ve ct or ();

Pošto 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 hoćete da koristite i neš-
to d ru g o iz biblioteke java.util.
Sukobe im ena biste m ogli sprečiti 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 slučaju,
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.)
Vežba 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 dešavaju.
166 Misliti na Javi

Lična biblioteka sa alatkama


Pošto 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 količinu teksta koji m o ram o da piše-
m o. Neka bu d e deo klase p o d im en o m Print, tako da na kraju dobijem o čitljiv statički 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 određenja
/ / 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 pređi 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 pređi 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 skraćenice m ožete 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, statičke
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 izvršavanja. 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 određenih 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 početak, i n t k r a j) {
i n t sz = k raj - početak;
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 ] = početak + 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 početak, i n t k r a j , i n t korak) {
i n t sz = ( k r a j - početak)/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 ] = početak + (i * korak);
return r e z u l t a t ;
}
} / / / =-

O d sad a m ožete u svoju bib lio tek u d o d ati svaku k o risn u novu alatku kada god naiđete
na nju. B iblioteci n e t.m in d v ie w .u til do d av aćem o k o m p o n e n te kroz celu knjigu.
1 68 Misliti na Javi

Korišćenje uvoženja za promenu ponašanja programa


U Javi nedostaje uslovno prevođenje iz C -a koje o m og ućava 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 ašan 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 najćešće ko rišćen a za
rešavanje p itan ja različitih 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 različiti delovi koda. Pošto 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 najčešće ko-
risti za ispravljanje grešaka u p ro g ra m u . Funkcije za o tk lan jan je grešaka 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 grešaka ili verziju za isp o ru k u . O va teh n ik a m ože da se k oristi
za bilo koju v rstu uslo v n o g prevođenja.
Vežba 3: (2) N ap rav ite dva paketa: debug i debugoff koji sadrže 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 ništa. 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 pokažite na d elu u slovno prevođenje.

Upozorenje pri radu s paketima


Treba zap am titi sledeće: 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 retraživ an jem p ro m en ljiv e CLAS-
SPATH. E ksperim entisanje rezervisanom reči p a ck ag e u p o četk u m ože da zb u n i jer će se,
ako se ne držite p ravila o im en u paketa i im en u d ire k to riju m a , pojaviti m no štvo tajan -
stvenih grešaka p ri izvršavanju. Na p rim er, m ožete d a d o b ijete p o ru k u kako nije bilo m o -
guće naći 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 grešci, 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, znaćete
gde Ieži problem .
Povedite raču n a o to m e da se prevedeni kod često ne sm ešta u d ire k to riju m u kojem je
izvorni kođ, ali izvršnom 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 troliše 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 način, za sve je definisana neka vrsta p ristu p a. U n ared n im odeljcim a, nauči-
ć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 znači da sve ostale klase iz tekučeg 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 ). Pošto
jedinica za prevođenje - 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 učav a da g ru pišete 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 išete 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 vašem vlasništvu treb a d a im a p ak e tn i p ristu p o stalo m k o d u u vašem
vlasništvu. M ože se reći 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 ožete svakojako da organ izu jete, ali ste u Javi p risiljen i da ih or-
ganizujete n a razu m an način . 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 ontroliše odakle se m ože p ristu p a ti n jen im člano vim a. K od iz d ru g o g pak eta n e
m ože da se pojavi, kaže: „Z dravo, ja sam Perin p rijatelj“ i očekuje da će d o b iti p ristu p zaš-
tićenim , 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 sledeće načine:
1 . C lan proglasite jav n im (p u b lic). Tada svako i sa svakog m esta m ože da m u p ristu p i.
2. Ć lan u om og u ćite paketni p ristu p izostavljajući 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 korišćenje klasa posv ećen om n asleđivanju,
p o to m a k klase m ože da p ristu p a i zaštićenim 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 ože da p ri-
stu p a saino ako su te dve klase u istom p ak etu . O nasleđivanju i zaštićenim
č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 suštinski je značajan za zrn a Jave, kao što ćete videti u poglavlju Grafička ko-
risnička okruženja.

public: interfejs za pristup


Kada u p o treb ite rezervisanu reč p u b lic , označavate 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 definišete paket d e se rt koji sadrži sledeću 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. Pogrešićete 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 tačaka za pretraživanje. Ako ne navedete tačku kao je d n u o d p u ta n ja u
svojoj prom enljivoj CLASSPATH, Java neće 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 ožete 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 ogućava
p ristu p sam o klasam a iz paketa d e se rt, pa će vas prevodilac sprečiti da je u p o treb ite.

Podrazumevani paket
M ožda će vas iznenaditi kada o tk rijete da će naredni p ro g ra m biti ispravno preveden iako
na prvi pogled izgleda da krši 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 prevođenje (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 ože 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 zaključka
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 izričito 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 označava da nekom članu niko ne sm e da p ristu p a osim klase
koja sadrži 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 ičn 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 utiče na d ru g e klase
iz istog paketa.
P odrazum ev an i paketni p ristu p često obezbeđuje 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 bično 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 ičn o da razm išljate o p ristu p u članovi-
m a koje izričito želite da proglasite javnim i d o stu p n im p ro g ra m e ru klijentu. U početku
verovatno nećete često razm atrati u p o treb u rezervisane reči private, jer m ožete da p ro đ ete
i bez nje. Ispostavlja se, m eđ u tim , da je d osledna u p o treb a rezervisane reči private veom a
važna, p o seb n o za višenitni rad (kao što ćete videti u poglavlju Paralelno izvršavatije).
Siedi p rim e r u p o tre b e rezervisane reči 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 ože zgodno da p o-
služi: m ožete da poželite da kontro lišete pravljenje objekta i da sprečite 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 ožete 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 zadržavate.
Isto važi 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 ožete 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 znači da neki d ru g i objek at ne m ože 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 reži.)

protected: „pristup nasleđivanjem"


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 nasleđi-
vanja (poglavlje Ponovno korišćenje klasa). Ali, opšte slike radi, biće d a t k ratak opis i
p rim e r korišćenja rezervisane reči 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 nasleđivanje, u kom e se
uzim a postojeća klasa i njoj d o d a ju novi članovi, bez izm ena na postojećoj klasi (koju
ćem o zvati osnovna klasa). T akođe, m o žete da izm en ite p o n ašan je postojećih članica klase
kada su u novoj klasi. Da b iste nasledili o d re đ e n u klasu, kažite da nova klasa pro širu je
(engl. e.rtends) tu p o sto jeću 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 aćete p ristu p
sam o javn im članovim a iz o rig in a ln o g paketa. (N aravno, ako nasleđivanje u rad ite u istom
paketu, m oći ć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 poželi 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 stiže rezerv isan o m rečju 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 zaštićenim (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 reči).
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, videćete da sledeća klasa ne m ože 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 važnih o so b in a n asleđivanja je sledeća: 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 pošto 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 sledeći način:

//: 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(); } // Zaštićena 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.


Vežba 4: (2) Pokažite da zaštićene m e to d e im aju p a k e tn i p ristu p iako nisu javne.
Vežba 5: (2) N ap rav ite ldasu s jav n im , p riv a tn im , zaštićenim č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.
Vežba6: (1) N apravite klasu sa zaštićenim 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 zaštićenim 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 obeležjim a i o d re đ e n im p o n ašan jem .
K ontrola p ristu p a n am eće o g ran ičen 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 slučajno
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 više 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 ožete da m enjate sve što nije jav n o (znači sve to je prijateljsko, za.š-
tićeno 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 zaštićene, p ak etn e i p riv atn e. P red n o st ovog načina je to što kori-
snik klase m ože 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 ože 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 ično olakšati č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 održava alatka Javadoc u m a n ju ju značaj čitljivosti prog ram a
za prog ram era klijenta. Prikazivanje interfejsa k o risniku klase u stvari je posao čitača klasa
(engl. class browser), alatke čiji je zadatak d a nap rav i pregled svih raspoloživih klasa i pokaže
šta se s njim a korisno m ože u rad iti (tj. koje članice su d o stu p n e). Prikazivanje d o k u m en ta-
cije razvojnog okruženja za Javu p o m o ć u čitača 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 ože 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 reči class.
Stoga m ožete d a napišete:

public class Spravica {

Ako je im e vaše biblioteke p ris tu p , svaki p ro g ra m e r klijent m ože da p ristu p i klasi


Spravica kada napiše:

import pristup.Spravica;

import pristup.*;

M eđu tim , postoje d o d a tn a ograničenja:


1. U svakoj jedinici za p rev o d en je (datoteci) m ože 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 ože 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 više o d jed n e
javne klase, prevodilac će p rijaviti grešku.
2. Im e jav ne klase m o ra ta čn o da se poklap a sa im en o m datoteke koja sadrži jed in icu
za prevođenje, uklju ču ju ći m ala i velika slova. Z nači, 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 slažu, p o n o v o ćete d o b iti p o ru k u o grešci.
3. M oguće je, iako nije uo bičajeno, da im ate je d in icu za p rev ođ en je u kojoj uopšte ne-
m a javne klase. U to m slučaju, d ato tek u m ožete 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 ržavaju 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 ećete d a pišete 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 zadržali 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 ože d a se k o risti sam o u n u ta r paketa.)
Vežba 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 ođa 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. Pošto 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 reći prevodilac.
Z ap am tite da klasa n e m ože 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 zaštićena.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 sprečiti sve osim sebe da n aprave ob jek at te klase. (I vi ćete to m oći sam o u n u ta r
statičn e članice te klase.) Evo p rim era:

//: pristup/Rucak.java
// Pokazuje specifikatore pristupa k la s i. Klasu pretvarate
// u privatnu proglašavajući njene konstruktore privatnim:

class Supal {
private Supal() {}
// (1) Dozvoljava pravljenje statičkom metodom:
public s ta tic Supal napraviSupu() {
return new Supal( ) ;
}
class Supa2 {
private Supa2() {}
// (2) Napravi statičn 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 aštič e n a, ali to je s p ec ija la n slučaj. Te klase će
b iti o b ja šn je n e u p o g la v lju Unutrašnje 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 može 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, većina m e to d a je vraćala 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 ože m alo da zbuni. Reč Supal p re im ena m eto d e (napraviSupu) g o v o ri šta m e-
toda vraća. Do sada je u ovoj knjizi to najčešće bila reč void, što znači da ne vraća ništa.
Ali tak o d e m ožete da vratite i referencu na objekat, što se ovde i đešava. M eto d a vraća re-
ferencu na objekat klase Supal.
Klase S u p a l i S u p a2 pokazu ju kako se proglašavanjem 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 izričito 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 napišete p o d razu m ev an i k o n stru k to r, sprečićete d a se o n a u to -
m atski napravi. U koliko ga zatim proglasite p riv atn im , niko neće 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 statična m etoda koja pravi novi objekat klase S u p a l i v ra-
ća referencu na njega. To m ože 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 graničili n jih o v u p o p u laciju ).
U S u pa2 koristi se projektni obrazac, o čem u više m ožete 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 statični i priv atn i član klase S upa2, pa p o stoji jed a n i sam o jed an
i njem u m ožete 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 znači da b ilo koja d ru g a klasa u paketu
m ože 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 izričitu 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 ,
statični član te klase javni, p ro g ram e r klijent još uvek m ože da p ristu p a sta tič n im člano-
vim a, iako ne m ože da n ap rav i objekat te klase.
178 Misliti na Javi

Vežba 8: (4) P rateći 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 izričito p rav i objekte klase Veza; treb alo b i da ih d obija sam o
prek o statičn e m eto d e iz klase UpravljacVezama. Kada klasi UpravIjacVezama p o n esta-
ne objek ata, o n a treb a da vraća referencu null. Klase testirajte u m e to d i main().
Vežba9: (2) N apravite sledeću 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 vašoj 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 sledeću 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 zašto prevodilac p rijavljuje grešku. Da li bi se išta p ro m e n ilo ako bi klasa


Strana p ostala deo paketa pristup.lokal?

Sažetak
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 vašu b iblioteku da bi n ap rav io neku aplikaciju ili još
veću 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 više voleli da ne rade d irek tn o s nekim od njih. Sve bi bilo
p o tp u n o izloženo.
O vo poglavlje se o d n o silo na pravljenje klase da bi se fo rm irala biblioteka: prvo, na
koji način se klase p ak u ju u je d n u bib lio tek u i dru g o , način na koji klasa kontroliše 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 rže 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 razluče šta je za njih b itn o , a šta m o g u d a ignorišu. Z bog to g a bolje ra z u m e ju klase.
D ru g i i najvažniji 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 način , a zatim otk rijete kako će se re-
k o n stru k cijo m k oda b itn o u b rz ati izvršavanje. Ako su interfejs i realizacija jasn o
razd v o jen i i zaštićeni, to m ožete 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 išu 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 greške. Bez ob zira na to koliko pažljivo p lan ira te i p ro -
jek tu jete, naprav ićete greške. Ako zn ate da greške neće izazvati m n o g o štete, bićete više
rasp olo ženi za eksperim ente, brže ćete učiti i brže završavati 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-
važniji 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 ožete 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 pažn 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 eđu au to ra biblioteke i n jenih spoljnih klijenata. Im a m n o g o situacija
u kojim a to nije slučaj. P rim era radi, sam i pišete sav kod ili rad ite zajedno s m a lo m eki-
p o m i sve što napišete ide u isti paket. U tim situacijam a p o tre b n a je drugačija k o m u n i-
kacija, pa k ru to prid ržavan je pravila p ristu p a m ože 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 .
Rešenja o d a b r a n i h vežbi 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 korišćenje klasa
Jedna od najlepših m ogućnosti u Javijeste ponovno korišćenje koda. Ali, da biste u tom p o slu
bili revolucionarni, sa kodom m orate u m eti da uradite m nogo više 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, rešenje 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 postojeće klase koje je n eko već n a p ra v io i o čistio o d grešaka.
Rešenje je d a se u p o tre b e klase b ez m e n ja n ja p ostojećeg k o d a. U o v o m poglavlju n au -
čićete dva n ačin a d a to p o stig n ete. Prvi je p rilič n o jasan: p ra v ite objekte postojeće 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 -
stojećih 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 jeće klase. Vi b u k v aln o uzi-
m ate o b lik postojeće klase, d o d ajete jo j k o d i n išta n e m e n ja te n a njoj sam oj. O va teh n ik a
se naziva nasleđivanje i p rev o d ilac tu obavlja veći d eo posla. N asleđivanje (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 istražen 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 nasleđivanja sličan (što
im a sm isla, je r se oba k o riste za pravljenje n o v ih tipova o d p o sto jećih ). U ovom poglavlju
učićete o tim m e h a n izm im a p o n o v n o g korišćenja koda.

Sintaksa kompozicije
D o ovog m esta u knjizi često sm o k oristili k o m p o ziciju - sm eštan 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 definišete:

//: 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 korišćenje 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 eću je kako p okušavate da na objek at tipa String ("izvor = ") nadovežete
objekat tipa Izvor. Pošto na objek at tip a String m o žete da nadovežete sam o d ru g i takav
olijekat, o n kaže: „pretv oriću Izvor u tip String po ziv an jem m e to d e toString()“. N akon
toga, o n m ože 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 statićn 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 ašan 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 rednošću
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 , izazvaćete izuzetak - grešku to k o m izvršavanja. D o b ro je što ipak m ožete
da ispišete 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 slučajevim a izazvalo n ep o tre b n e režijske troškove. 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 definišu. To znači 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 režijske troško 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 = "Srećan",
s2 = "Srećan",
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) // Odložena 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 korišćenje klasa 183

p rin t(b );
}
} /* Ispis:
Unutar klase Kada()
Sapun()
sl = Srećan
s2 = Srećan
s3 = Radostan
s4 = Radostan
i = 47
igracka = 3 . 1 4
sapuncic = Konstruisan
* ///:-

O b ra tite p ažn ju na to da se u k o n stru k to ru klase K ad a n a re d b a izvršava 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 pošaljete p o-
ru k u - izuzev neizbežnog izuzetka p rilik o m izvršavanja.
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 korišćenja.
Vežba 1: (2) N apravite jed n o stav n u klasu. U n u ta r d ru g e klase definišite referencu objekta
prve klase. Za in stan ciranje tog objekta k o ristite len ju in iđ jalizaciju .

Sintaksa nasleđivanja
N asleđivanje 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 nasleđivanje. 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 oćigledna, ali se za n asleđivanje up otreb ljav a p o seb n a sin tak -
sa. Pri nasleđivanju kažete „nova klasa ie kao ta sta ra klasa“. U p ro g ra m u to navodite pre
p o ćetn e vitićaste zagrade tela klase, pišući rezervisanu reć e x te n d s iza koje sledi im e
osnovnc kluse. Kada to učinite, 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 nasleđivanja.
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 više oso b in a. Prvo, u m eto d i dodaj klase Cistac, objekti klase String
se nadovezuju na sadržaj 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 sadrže m eto d u inain(). Svaka o d vaših klasa
m ože d a sadrži in eto d u main(); teh n ik o m stavljanja m eto d e main() u svaku klasu, o m o -
gućavate lako testiran je svake klase. Kada ga završite, m e to d u main() ne m o rate da ukla-
njate; m ožete d a je o stavite za kasnije testiranje.
I kada im ate p u n o klasa u p ro g ra m u , biće 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 slučaju biti pozvana m etoda Deterdzent.main() ako
napišete java Deterdzent. M eđ u tim , tak o đ e m ožete da napišete 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 slučaju o b ra tite p ažn ju na to da m eto d a Deterdzent.main() izrićito 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 ožete da pro sled ite bilo koji niz elem en ata tipa String).
Važno 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 korišćenje 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 ogućili nasleđivanje klase,
opšte 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 ože da p ristu p a zaštićenim članovim a; o to m e nešto kasnije). N aravno d a u o d ređ en im
slučajevim 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(). Pošto je klasa Deterdzent izvedena iz klase Cistac (p o m o ću rezervisane reči
extends), njen interfejs au to m atsk i sadrži i navedene m eto d e, iako ne v id ite d a su o n e u
njoj izričito definisane. Z nači d a nasleđivanje 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 uće m en jati m e to d u koja je već bila defi-
nisan a u o sn o vno j klasi. U ovom slučaju, 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 rešila ovaj p ro b lem , Javina rezervi-
sana reč super označava natk lasu (engl. superclass) koju tek u ća klasa nasleđuje. Stoga n a-
redba super.ribajO poziva verziju m eto d e ribaj() iz o sn o v n e klase.
Pri n asleđivanju 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 način na koji b iste to radili i s bilo k o jo m d ru g o m
klasom : definišite 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 Deterđzent m o žete da p o -
zivate m eto d e koje su raspoložive za klasu Cistac i o n e za klasu Deterdzent (na prim er,
zapeni()).
Vežba 2: (2) Iz klase D e te rd z e n t nasledite novu klasu. Redefinišite 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


Pošto su sada u p riču uključene 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 ožda 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 sadrži 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 način 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 nasleđivanja:

//: ponovnaupotreba/Karikatura.java
// Poziv konstruktora pri nasleđivanju.
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 ože 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.
Vežba3: (2) D okažite 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).
Vežba4: (2) D o kažite 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.
Vežba 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 orišćeni 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 izričito po-
zvati k o n stru k to r o sn o v n e klase koristeći rezervisanu reč s u p e r i proslediti m u odgova-
rajuću listu arg u m en ata:

//: ponovnaupotreba/Sah.java
// Nasleđivanje, konstruktori i argumenti.
import s ta tic net.m indview.uti1 .P r in t .*;
Poglavlje 7: Ponovno korišćenje 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 ože 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 pogrešite.)
V ežba 6: ( 1) P om oću dato tek e S ah .jav a d okažite tv rd n je iz p re th o d n o g pasusa.
V ežba 7: ( 1) Izm enite vežbu 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 apišite k o n stru k to r klase C i svu inicijalizaciju obavite
u n u ta r njega.
V ežba 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.
Vežba 9: (2) N apravite klasu p o d im en o m K oren koja sadrži 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.
Vežba 10: ( 1) Izm enite p re th o d n u vežbu 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 podržava d ire k tn o treći o d n o s koji se naziva delegiratije. To je nešto na pola p u ta
izm eđu nasleđivanja i k om pozicije, p o što u klasu k o ju p ra v ite sm eštate 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 nasleđivanje). N a p rim er, svem irskom b ro d u treb a m o d u l za upravljanje:

//: ponovnaupotreba/UpravljačkiUređajiSvemirskogBroda.java

public class UpravljačkiUređajiSvemirskogBroda {


void naviše(int brzina) {}
void naniže(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 način a d a se n a p ra v i svem irski b ro d bilo b i nasleđivanje:

//: ponovnaupotreba/SvemirskiBrod.java

public class SvemirskiBrod extends UpravljačkiUređajiSvemirskogBroda {


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 Ijačk 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 ožete „narediti"
da ide n a p red (). Tačnije je reći da S v em irsk iB ro d sadrži U p rav Ijačk 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 ljačkiU red ajiS v em irsk o g B ro d a.
O vu n ed o u m icu rešava delegiranje:

//: ponovnaupotreba/DelegiranjeSvemirskogBroda.java

public class DelegiranjeSvemirskogBroda {


private String ime;
private UpravljačkiUredajiSvemirskogBroda upravljačkiUređaji =
new Upravl jačkiUređajiSvemirskogBroda();
public DelegiranjeSvemirskogBroda(String ime) {
this.ime = ime;
}
// Delegirane metode:
public void nazad(int brzina) {
Poglavlje 7: Ponovno korišćenje klasa 189

upravljačkiUređaji.nazad(brzina);
}
public void naniže(int brzina) {
upravljačkiU ređaji.naniže(brzina);
}
public void napred(int brzina) {
upravljačkiUređaji.napred(brzina);
}
public void nalevo(int brzina) {
upravljačkiU ređaji.nalevo(brzina);
}
public void nadesno(int brzina) {
upravljačkiUređaji.nadesno(brzina);
}
public void turboPotisak(int brzina) {
up ravljačkiUređaji.turboPotisak(brzina);
}
public void naviše(int brzina) {
upravljačkiU ređaji.naviše(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 nasleđivanju. M eđ u tim , deleg iran je o m o g u ću je više k o n tro -
le, p ošto u objekat član m ožete p ren eti proizvoljan p o d sk u p svih m etoda.
Iako Java ne podržava delegiranje, im a m n o g o razv o jn ih alatki koje ga p održavaju. 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 ežba 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 nasleđivanja


K om pozicija i nasleđivanje se često koriste zajedno. N ared n i p rim e r po k azu je pravljenje
složenije klase, korišćenjem 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 nasleđivanja.
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 način da se nešto 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 korišćenje 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 raču na.
P rilično je zadivljujuće 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 većini slučajeva treb a sam o d a uvezete
paket. (O vo važi i za nasleđivanje i za ko m poziciju.)

Garantovanje pravilnog čišćenja


Java n em a ništa slično destruktoru iz C + + -a, tj. n em a m e to d u koja se au to m atsk i poziva
pri u n ištav an ju objekta, verovatno zato što je u Javi prak sa da objekte zabo rav ite u m esto
da ih u ništavate i da p u stite sakupljač sm eća 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 slučajevim a klasa m ože d a obavlja izvesne aktiv-
nosti koje zahtevaju čišćenje. Kao što je p o m e n u to u poglavlju Inicijalizacija i čišćenje, vi
ne m ožete znati kada će sakupljač sm eća biti pozvan ili da li će u op šte biti pozvan. Stoga,
ako je za neku klasu p o tre b n o čišćenje, m o rate izričito da napišete 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 grešaka pom oću izuzetaka - o d izuzetaka m o ra te d a se
zaštitite tako što ćete odg o v araju ću p ro c e d u ru za čišćenje 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
// Obezbeđivanje pravilnog čišćenja
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("Č išćenje 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 korišćenje klasa 193

// Poredak čišćenja 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
Č išćenje klase Obli k
Brisanje kruga
Či šćenje klase Obli k
Brisanje li n i j e : 2, 4
Čišćenje klase Obli k
Brisanje li n i j e : 1, 1
Čišćenje klase Oblik
Brisanje li n i j e : 0, 0
Čišćenje klase Oblik
Čišćenje 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-
sleđuje k orensku klasu). Svaka klasa redefiniše 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 reči 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 ože da u rad i nešto što zahteva
čišćenje. Svaka klasa im a svoju m e to d u ciscenje() koja vraća 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 reči koje neće biti d etaljn o
o b jašnjene sve d o poglavlja O brada grešaka pom oću izuzetaka, a to su try i finally. Rezer-
visana reč try naznačava da je b lo k koji sledi (u n u ta r v itičastih zagrada) zaštićeni region,
što znači 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 zaštićenog regiona. Taj k o d se uvek izvršava, 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 ože se n ap u stiti na
više n eu o biča jen ih načina.) U o vo m slučaju, b lo k finally kaže: ,,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 čišćenje
(u ovom slučaju, m e to d i ciscenje()) tak o đ e m o rate da v odite ra č u n a o p o retk u čišćenja
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 čišćenje k oje je specifično za vašu
klasu, i to red osled o m koji je o b rn u t o d redo sled a pravljenja. (U o p štem slučaju, 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 ašem p rim e ru p o k azan o .
U m n o g im slučajevim a čišćenje nije p ro b le m i m o žete d a p u stite sakupljača sm eća da
obavi posao. Ali, kada je izričito čišćenje p o tre b n o , n je m u m o ra te da p ristu p ite m arljivo
i pažljivo, p o što n a sak u p ljan je sm eća ne m ožete previše d a se oslo n ite. Sakupljanje sm eća
u o pšte n e m o ra da se d o go d i, a ako se d o g o d i, m ože d a čisti objekte bilo kojiin redosle-
d o m . N ajbolje je n e oslan jati se na sakupljač sm eća ni za šta o sim za o slobađanje m em o -
rije. Ako v am je čišćenje 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 ežba 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 iše 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 korišćenje 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 ročitati u n ared n o m poglavlju, m eto de istog im ena se m n o g o češće redefinišu 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. Inače rezultati m o g u biti
zbun juju ći (zbog čega to u C++-U i nije dozvoljeno, da ne biste napravili grešku).
U Javi SE5 d o d a ta je an o tacija @Override, što nije rezervisana reč, ali se m ože u p o -
trebljavati kao da jeste. Kada hoćete da redefinišete m eto d u , m ožete d o d ati tu an o tac iju i
prevodilac će prijaviti grešku ukoliko je n e n a m e rn o preklo p ite um esto da je redefinišete:

//: ponovnaupotreba/Lisa.java
// {Compi1eTimeError} (Neće 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 prevođenje p ri-
m era iz knjige, ali ako p ro b a te ru č n o da prevedete taj p rim e r d obićete p o ru k u o grešci:

method does not override a method from it s superclass

T im e će anotacija <®Override sprečiti da slučajno preklo p ite m e to d u koja nije bila


pred v iđ en a za to.
196 Misliti na Javi

V ežba 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 ažite d a su sve četiri m eto d e d o stu p n e u
izvedenoj klasi.

Izbor između kompozicije i nasleđivanja


I kom pozicija i nasleđivanje 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 -
stojeće 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 jeće 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 ošto 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 više delova, lakše ć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 pomoću 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 korišćenje 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);
}
} ///= -
Pošto je k o m p o z iđ ja kola u ovom slučaju deo analize p ro b le m a (a n e sam o d eo osnov-
no g p ro jek ta), proglašavan jem članova jav n im p o m aže 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 napiše.
Im ajte n a u m u da je ovo specijalan slučaj i d a polja u g lav n o m treb a da b u d u p riv atn a.
Pri nasleđivanju polazite o d postoječe klase i p rav ite n je n u specijalnu verziju. U
o pštem slučaju, to znači 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, uvidečete 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 sadrži vozilo, o n jeste vozilo. Relacija jesteizražav a se na-
sleđivanjem a relacija sadrži izražava se k o m p o zicijo m .
V ežba 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


Pošto ste savladali nasleđivanje, rezervisana reč p ro te c te d n ap o k o n d o b ija značenje.
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 nešto 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 kaže: „O vo je p riv a tn o što se
tiče 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 zaštićeno (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 zaštićena 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 ržite pravo da m en jate o sn o v n u realizaciju. Tada p o m o ć u zaštićenih m eto d a m ožete
da dozvolite k o n tro lisan p ristu p n asled n icim a vaše 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 zaštićena
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 ožete 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 zaštićena.


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.
Vežba 15: (2) N ap rav ite klasu sa zaštićen o m m e to d o m u n u ta r paketa. P okušajte da pozo-
vete tu zaštićenu m e to d u van paketa i o bjasnite rezultate. Sada n asledite tu klasu i pozo-
vite zaštićenu m e to d u iz neke d ru g e m eto d e te nasleđene klase.

Svođenje naviše
N ajbitniji vid nasleđivanja nije o b ezbeđivanje 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 ože da se sažm e kao „nova
klasa je tipa postojeće klase“.
P reth o d n i opis nije izm išljen sam o da bi se objasnilo nasledivanje - jezik ga d irek tn o
podržava. 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. Pošto nasleđivanje 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 ožete da pošaljete osnovnoj klasi,
m ožete da pošaljete i izveđenoj klasi. Ako klasa Instrument im a m eto d u sviraj(), im aće je
i Duvacki in stru m en ti. To znači da, bez greške, m ožem o reći kako je objek at tipa Duvacki
takođe i tip a Instrument. N aredni p rim e r pokazuje kako prevodilac p održava tu notaciju:
Poglav[je 7: Ponovno korišćenje klasa 199

//: ponovnaupotreba/Duvacki.java
// Nasleđivanje i svođenje naviše.
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); // Svođenje naviše
}
} ///:-

U o v om p rim e ru važna 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 prosleđiva-
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 takođe i objekat tip a Instrument. Z bog toga n e po sto ji
m e to d a m elodija() kojoj m ože 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 svođenje naviše (engl. upcasting).

Zašto „svođenje naviše'?


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 nasleđivanja za D u v ack i.ja v a je:

K onverzijom od izvedene ka osnovnoj klasi krećete se naviše k roz dijagram nasleđiva-


nja, pa se to često zove svođenje naviše. Svođenje naviše je uvek bezb ed n o , je r se sa o d re-
đenijeg tip a vraćate na opštiji; o d n o sn o , izvedena klasa je natklasa osn o v n e klase. O n a
m ože d a sadrži više m eto d a nego osn o v n a klasa, ali zato mora da sadrži o n e m eto d e koje
p o sto je u osno v noj klasi. Prilikom svođenja naviše, je d in o se m ože 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 svođenje naviše


bez eksplicitne konverzije ili neke d ru g e p o se b n e notacije.
O sim svođenja naviše, m o žete da u ra d ite i su p ro ta n proces, svođenje naniže, ali se p ri
to m e javljaju o d re đ e n e dilem e, o čem u če biti reči u n a re d n o m poglavlju i poglavlju
Podaci o tipu.

Ponovo o biranju između kompozicije i nasleđivanja


N ajčešči način za pravljenje i korišćenje 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 oristićete i p ostojeće klase za iz-
građ iv an je no v ih klasa k o m p o zicijo m . N asleđivanje ćete ređe k oristiti. Iako je p ri
p o d u čav an ju O O P -a p riličn o n aglašeno nasledivanje, to n e znači da ga treb a k o ristiti gde
go d je m oguće. 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 nasleđivanie
ako se zap itate h o će li v am ikada b iti p o tre b n o svođenje naviše iz nove klase u o sn o v n u
klasu. A ko m o ra te da k o ristite svođenje naviše, nasleđivanje je n e o p h o d n o , ali ako ne m o -
rate, pažljivo p ro u čite d a li v am n asleđivanje zaista treba. U poglavlju Polim orfizam opi -
suje se je d a n o d o sn o v n ih razloga za svođenje naviše. 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 svođenje naviše?“, u ru k a m a ćete im ati d o b ru alatku za o dluči-
vanje izm eđ u k om pozicije i nasleđivanja.
Vežba 16: (2) N ap rav ite klasu p o d im en o m Vodozemac. Iz nje nasledite klasu Zaba. Sta-
vite odgovarajuće 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 naviše d o klase Vodozemac i p okažite da sve m eto d e i dalje rade.
Vežba 17: (1) Izm enite p re th o d n u vežbu tako da u klasi Zaba redefinišete m e to d u iz
osn o v n e klase (n ap ra v ite nove definicije koristeći iste p o tp ise m eto d a). Pogledajte šta se
dešava u m eto d i main().

Rezervisana reč final


Z načen je Javine rezervisane reči 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 ože da se m enja“. M ožete poželeti da p ro m en e
sprečite zbog p ro jek ta ili efikasnosti. Pošto su ti p rin cip i sasvirn različiti, rezervisana reč
final m ože se i p o g rešn o u p o tre b iti.
U n a re d n o m o deljku razm atraju se tri slučaja u kojim a m ože 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 ačin da p revodiocu naznače 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 prevođenju koje se nikada neće p ro m en iti.
2. M ogu da predstavljaju v red n o sti koje inicijalizujem o to k o m izvršavanja, a ne želi-
m o da b u d u m enjane.
Poglavlje 7: Ponovno korišćenje 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 izraču n av an ja u k ojim a se pojavljuje, to jest raču n an je m ože d a se
obavi to k o m prevođ en ja, čim e se ubrzava izvršavanje. 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 označava se p o m o ć u rezervisane reči 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 statičn o i finalno p ostoji sam o je d n o m esto za sldadištenje i o n o ne
m ože 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 značenje m ože da zbuni. Uz p ro s t tip, final označava 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
poveže sa o b jek to m , o n a više ne m ože 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 ože d a se izm eni - Java ne o b ezb eđ u je n ačin d a neki
proizv o ljan o b jekat p retv o rite u k o n stan tan . (M ožete, ipak, svoju Idasu d a napišete tako
da se n jen i o b jek ti efektivno p o n ašaju k ao d a su k o n stan tn i.) O vo og ran ičen 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 ažnju na to da se statičn a i finalna polja (tj. ko n -
stan te p ri p rev o đ en ju ) p o konvenciji pišu velikim slovim a, s tim da se iz m e đ u reči piše
p o tcrta.

// : ponovnaupotreba/Finalni Podaci.java
// Efekat rezervisane reči 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 prevođenju
private fin al int vrednostJedan = 9;
private s ta tic fin al in t VREDNOST_DVA = 99;
// Tipična javna konstanta:
public s ta tic final in t VREDNOST_TRI = 39;
// Ne mogu da budu konstante pri prevođenju:
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++; // Greška: vrednost ne može 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); // Greška: ne možete 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
* ///:-

Pošto 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 uobičajeniji način za definisanje takvih
konstanti: kao javne, zbog čega m o g u da se koriste i izvan paketa, statične, da bi se nagla-
silo kako postoji sam o je d n a kopija i finalne, da se naznači da su k on stantne. O b ratite
pažnju n a to da su finalne i statične 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
reči su razdvojene p o tc rto m (kao i k on stan te u C -u, odakle i potiče ova konvencija).
Sam o zato što je nešto finalno, ne znači da je njegova vredn ost p o z n a ta u tre n u tk u pre-
vođenja. U p rim eru , to je po k azan o na prom enljivam a i4 i INT_5 koje su inicijalizovane
slučajnim brojevim a to ko m izvršavanja. Taj deo p rim era tak ođ e p okazuje u čem u je ra-
zlika izm eđu statičn ih i nestatičn ih finalnih vrednosti. Razlika se javlja sarno kada se vred-
nosti inicijalizuju prilikom izvršavanja, jer sve k on stan te p ri prev ođ enju prevodilac isto
tretira. (I verovatno o p tim izu je i elim iniše, jer više nisu p o treb n e.) Razlika se vidi iz re-
zultata izvršavanja 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 statična, pa se
inicijalizuje sam o je d n o m pri učitavanju klase, a ne svaki pu t kada se pravi novi objekat.
Prom enljive o d vl do VRK 3 pokazuju značenje finalnih referenci. Kao što m ožete vi-
deti u m etodi main(), to što je v2 finalna, ne znači da ne m ožete m enjati vrednost objekta
na koji ukazuje. Ali pošto je v2 referenca, rezervisana reč final znači da v2 ne m ožete da
povežete s nekim dru g im objektom . Isto važi i za niz, koji je u suštini sam o druga vrsta
Poglavlje 7: Ponovno korišćenje klasa 203

reference. (Koliko ja znam , ne postoji način da sam e reference u nizu proglasite finalnim .)
Proglašavanje referenci finalnim čini se m anje korisnim o d proglašavanja prom enljivih
prostog tipa finalnim.
V ežba 18: (2) N apravite klasu koja im a statičn o finalno polje i finalno polje i p okažite raz-
like izm eđ u njih.

Prazna finalna polja


Java dozvoljava pravljenje praznih fina ln ih polja (engl. blank finals), što označava polja
koja su deklarisana kao finalna, ali im nije zadata inicijalizaciona v red n o st. U svim sluča-
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 slučaju m ože d a b u d e različito za svaki objekat, a da
ipak pri to m zadrži 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 način se g aran tu je da su finalna polja uvek in iđjalizov ana
pre upotrebe.
Vežba 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 okažite da finalni elem ent m o ra biti
inicijalizovan pre u p otreb e i da nakon toga više ne m ože da se m enja.
204 Misliti na Javi

Finalni argumenti
A rgum ente m ožete da pro g lasite fin aln im tak o što ih deklarišete k ao finalne u listi argu-
m enata. To znači da u n u ta r m e to d e n e m ožete m en jati o n o na šta taj a rg u m en t kao refe-
renca ukazuje:

//: ponovnaupotreba/FinalniArgumenti.java
// Upotreba rezervisane reči " 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 možete da promenite
// Finalnu promenljivu prostog tipa možete 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 dešava kada su arg um enti prostog tipa finalni: argum ent
m ožete čitati, ali ga ne m ožete pro m en iti. To se koristi prvenstveno za prosleđivanje poda-
taka an o n im n im u n u trašn jim klasam a koje čete u poznati u poglavlju Unutrašnje 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 ,,zaključa“ kako bi
se sprečilo da bilo koja klasa naslednica p ro m en i n jeno značenje. To se radi zbog projekt-
n ih razloga, kada želim o da b u d e m o sigurni kako če p on ašan je m etod e biti neprom enje-
n o p rilikom nasleđivanja, o d n o sn o kako m eto d a ne m ože biti redefinisana.
Ranije se kao dru g i razlog za koriščenje 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 naišao na poziv finalnoj m etod i, m ogao je (p o svom n a h o đ e n ju ) da preskoči
uobičajen poziv (stavi arg u m en te na stek, skoči na kod m eto de i izvrši ga, vrati se, počisti
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 režijski troškovi
sam og poziva. N aravno, ako je m etod a velika, kod počinje da se povečava i verovatno ne
Poglavlje 7: Ponovno korišdenje klasa 205

biste d o b iti poboljšanje 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 izvršavanja m etode.
U novijim verzijam a Jave, v irtu e ln a m ašin a (ko n k retn o , tehnologije v ru ćih tačaka)
m ože 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 više ne treb a da p o m ažete 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 izvršno 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 izričito da sprečite n jen o redefinisanje.1

Finalno i privatno
Svaka p rivatna m etod a u klasi im p licitn o je i finalna. Pošto priv atn o j m eto d i ne m ožete
da pristu p ite iz nasleđenih klasa, ne m ožete n i da je redefinišete. P rivatnoj m eto d i m ožete
da dodelite specifikator final, ali njoj to ne daje nikakvo d o d a tn o značenje.
O vo stanje m ože da izazove zab u n u - ako pokušate da redefinišete p riv atn u m eto d u (ko-
ja je im plicitno finalna), to ćete sam o naizgled uspeti, jer prevodilac neće prijaviti grešku:

//: ponovnaupotreba/IluzijaRedefinisanjaFinalnih.java
// Privatnu i l i privatnu finalnu metodu samo naizgled možete da redefinišete.
import static net.mindview.uti1. P r in t.*;

class SadrziFinalne {
// Identično 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( ) " ) ; }
// Takođe 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 previše


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 reči 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 itiože 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();
// Možete da obavite svodenje naviše:
RedefinisanjePrivatnih op = op2;
// A li ne možete da pozovete metode:
//! o p .f( ) ;
//'■ o p .g ();
// Isto važi 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 ućno sti da objekat svedete naviše 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 slučajno im a isto im e kao m eto da u izveđenoj klasi. Ako napravite javnu m eto du ,
zaštićenu m e to d u ili m eto d u s p ak etn im p ristu p o m u izveđenoj klasi, ne postoji veza s pri-
v a tn o m m eto d o m koja m ože im ati takvo isto im e u osnovnoj klasi. Niste redefinisali
postojeću m e to d u , nego napravili novu. Pošto je privatna m etoda n e d o stu p n a i efektivno
nevidljiva, o n a ne utiče ni na šta sem na organizaciju koda klase u kojoj je definisana.
Vežba 20: (1) Pokažite da anotacija <®Override reša\'a problem razm otren u ovom odeljku.
V ežba 21: (1) N apravite klasu s fin aln o m m eto d o m . N asledite klasu i pokušajte da tu m e-
to d u redefinišete.

Finalne klase
Kada ozn ačite da je cela klasa finalna (stavljajući rezervisanu reč final pre njene definici-
je), saopštav ate da tu klasu ne želite da nasleđ u jete niti dozvoljavate bilo kom e da to učini.
D ru g im rečim a, iz nekog razloga p ro jekat vaše klase nikad se neće m enjati ili iz bezbed-
n o sn ih razloga ne želite da o m o g u ćite nasledivanje.

//: ponovnaupotreba/Preistorijski.java
// Proglašavanje cele klase finalnom.

class MaliMozak {}

fin al class Dinosaurus {


in t i = 7;
in t j = 1;
Poglavlje 7: Ponovno korišćenje klasa 207

MaliMozak x = new MaliMozak();


void f ( ) {}
}

//! class Nastavak extends Dinosaurus {}


// greška: Klasa 'Dinosaurus' ne može da bude proširena

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 važe ista pravila kao i ranije, bez o bzira na to da li je sam a klasa finalr.a. Kako je
nasleđivanje sprečeno, sve tnetode u finalnoj klasi su im p licitn o (au to m atsk i) finalne,
po što ne p o sto ji način 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 značenje.
V ežba 22: (1) N apravite finalnu klasu i p okušajte da je nasledite.

Pažljivo sa final
Kada p ro jek tu jete klasu, m ože vam se učiniti lcao d o b ra iđeja da m e to d u proglasite final-
n o m . M ožda p om islite da ionako n em a šanse da iko ikada ređefiniše vaše m etode. Pone-
kad je to zaista tako.
Ipak, pažljivo razm islite. U glavnom je teško pred v id eti kako će se klasa p o n o v o kori-
stiti, n aro čito klase opšte naraene. Ako m eto d u proglasite fin aln o m , sprečićete da vašu
klasu p u te m nasleđivanja 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 ože koristiti na taj način.
S ta n d a rd n a Javina biblioteka je d o b a r p rim er. N aročito 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 proglašene finalnim . Lako je p retp o stav iti kako
želite da nasledite i redefinišete tako fu n d am en taln o k o risn u klasu, ali su p ro je k tan ti ne-
kako odlučili d a to nije u redu. Iro n ičn 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š tačno s logičke tačke gle-
dišta. 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
način napravili Stack, treb alo je da shvate kako su finalne m eto d e previše ograničene.
D rugo, m noge najvažnije 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 izvršavanje, to p rav i značajne režijske troškove koji verovatno poništavaju svu korist o d
toga što su inetode finalne. O va priča p o tv rđ u je teo riju da p ro g ram eri loše p ogađaju m esta
na kojim a treb a o p tim izovati. Zaista je loše što je tako nesp retan projekat ušao u sta n d ard -
nu bib lio teku pa svi m orarn o da se b o rim o s njim . (Na sreću, kontejnerska biblioteka
208 Misliti na Javi

savrem ene Jave zam enjuje ldasu Vector klasom ArrayList, koja se po n aša m n o g o p rim ere-
nije. N ažalost, 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 ažno je n a p o m e n u ti d a klasa Hashtable, jo š je d n a v ažn 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ć rečen o n a d ru g im m estim a
u ovoj knjizi, priličn o je o čigledno d a su neke klase p ro jek to v ali sasvim različiti Ijudi. (Vi-
dećete i da su im ena m eto d a u klasi Hashtable m n o g o k raća 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 očigledno.
Kada stvari nisu dosledne, k o risn ik sam o im a više posla. To go v o ri d a p ro jek a t i k o d treb a
pregledati više 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 učitavanje klasa


P ro g ra m i pisani n a većini jezika učitav 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 pažljivo k o n tro lisan , kako p o re d a k inicijalizacije statičn 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 statični elem en t očekuje da d ru g i b u d e isp rav an p re nego što je taj d ru g i statični
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 učitav an ju . O vo je je d n a
od aktiv n osti koje su p ostale lakše 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 učitava sve d o k taj k o d ne
b u d e p o treb an . U opšteno, m o žem o reći da se ,,kod klase učitava na inestu prve u p o tre b e “.
O b ičn o se klasa učitava p rilik o m k o n stru k cije prv o g o b jek ta te klase, ali se učitavanje
obavlja i prilikom p ristu p a n ja statičk o m po lju ili statičkoj m e to d i.2
Prilikom prvog korišćenja klase, obavlja se i statičk a inicijalizacija. Svi statičn i objekti
i statični delovi koda inicijalizuju se u tre n u tk u učitav an ja p o „redosledu pisanja" (tj. u
p o retk u kojim ste ih napisali u definiciji klase). S tatični ele m e n ti se, n arav n o , inicijalizuju
sam o jed n o m .

Inicijalizacija i nasleđivanje
Da bism o dobili celovitu sliku o svem u ovom e, p o g le d a jm o ceo proces inicijalizacije
uključujući i nasleđivanje. 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 statična m etoda, m ada se rezervisana re ć s ta tic ne m ora eksplicitno navesti. Dakle,
preciznije je reći da se klasa učitava o n d a kada se pristupi biio kojem njen o m statičnom članu.
Poglavlje 7: Ponovno korišćenje 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 statična 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 statična 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 statična promenljiva Insekt.xl
in icijaliz o va n a statična 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 okušavate d a p ristu p ite (statičn o j) rnetodi Bu-
ba.main(), zbog čega prevodilac (u d atoteci Buba.class) traži p revedeni k o d klase Buba.
P ro gram za učitavanje (engl. loader) p rim eču je d a o n a im a o sn o v n u klasu (što označava
rezervisana reč extends), pa zatim učita 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 statička inicijalizacija u korenskoj o sn o v n o j klasi (u n ašem slučaju klasi
Insekt), zatim u sledećoj izvedenoj i tako redom . O vaj redosled je b itan , je r statička ini-
cijalizacija izvedene klase m ože da zavisi o d p rav iln e inicijalizacije nekog statičkog člana
osn o v n e klase.
U to m tre n u tk u , učitan e su sve n e o p h o d n e klase i objek at m ože 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 ašem slučaju,
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
reči 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 onačno, izvršava se o statak tela k o n stru k to ra .
Vežba 23: (2) D okažite da se klasa učitava sam o je d n o m . D o k ažite d a učitavanje m ože 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 statičn o m
članu.
Vežba 24: (2) U dato teci Buba.java, iz klase Buba n asledite specifičnu v rstu b u b e, držeći
se istog fo rm ata kao kod postojećih klasa. P ratite i objasn ite rezu ltat p ro g ra m a .

Sažetak
I nasleđivanje i kom pozicija o m o g u ćav a ju da o d po sto jećih tip o v a n a p ra v ite nove. K om -
pozicija upotrebljav a postojeći tip kao d eo o sn o v n e realizacije n o v o g tip a, a nasleđivanje
pon o v o u potrebljava interfejs.
U nasleđivanju, izvedena klasa o b u h v a ta interfejs o sn o v n e klase, p a m ože da b u d e sve-
dena navišc 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 aglašenom 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 triše te na k o m p o ziciju (ili na delegi-
ranje), a nasleđivanje 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 nasleđivanje na objekte članove, m o žete im p ro m en iti
tačan tip, a sam im tim i p o n ašan je to k o m izvršavanja. D ru g im rečim a, p o n ašan je objekta
nastalog p o m o ć u kom pozicije m o žete da m en ja te to k o m izvršavanja.
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 specifičnu 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 gućno 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 ožete da je koristite takvu kakva je već je m o rate p ro širiti). U koliko p ro jek at p o stan e
previše složen, često p o m aže kada d o d a te nove objekte nastale cep an jem p ostojećih na
m anje delove.
Kada počnete da p ro jek tu jete sistem , važno je da shvatite kako je razvoj p ro g ram a p o -
stu p an proces, baš kao i čovekovo učenje. N jegova osnova je ek sp erim e n tisan je; m ožete
b eskrajno dug o da an alizirate stvari, ali i dalje nećete znati sve o d g o v o re kada se u p u stite
u projekat. Im aćete m n o g o više u sp eh a - i više n ep o sred n ih p o d ata k a o rezu ltatim a - ako
,,uzgajate“ svoj projekat kao organsko biće koje se razvija, u m esto da ga o d je d n o m pravite
kao stakleni soliter. N asleđivanje i k o m pozicija sp ad aju m eđ u najvažnije 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 ešenja o d a b ra n ih vežbi 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 Bebidže, recite hoće li iz vaše m ašine izaći tačni odgovori
čak i ako u nju unesetepogrešne 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 EĆU 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 nasleđivanja.
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 poboljšava
čitljivost, p o lim o rfizam om o g u ćav a i pisan je proširivih 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 ašan ja. Sakrivanje
realizacije odvaja interfejs o d realizacije tako što detalje proglašava p riv a tn im . O vakva
m eh an ička 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 ućava 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 različitim tipo vim a. P olim orfni poziv m eto d e o m ogućava
da jed an tip izrazi svoju različitost o d d ru g o g sličnog tip a, dokle god su oba izvedena iz
istog pro stog tipa. Ta različitost se ispoljava u p o n a šan ju m eto d a koje m ožete 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 ičko vezivanje, kao i k a -
sno vezivanje ili vezivanje prilikom izvršavanja) poći ćete o d osnova, uz jed n o stav n e p ri-
m ere koji ne sadrže ništa d ru g o sem p o lim o rfn o g p o n ašan ja p ro gram a.

Ponovo o svođenju naviše


U p re th o d n o m poglavlju videli ste kako objekat m ože 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 svođenje naviše (engl. upcasting) zbog načina crta n ja stabala nasleđi-
vanja —sa o sn o v n o m klasom u vrh u .
Videli ste tak ođe da se pojavljuje i p ro blem p rikazan u sledećem p rim e ru s m u zičkim
in stru m e n tim a.
Prvo, pošto se u više 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 tnuzičkim instrumentima.
package polimorfizam.muzika

publi c enum Nota (


SREDNJE_C, C_P0VISEN0, B_SNIZEN0 ; // Itd .
> ///:-
212 Misliti na Javi

Nabrojane tipove pređstavili smo u poglavlju Inicijalizacija i čišćenje.


Ovde je Duvački tip Instrumenta; zato je Duvački nasleđen 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
//: Nasleđivanje i svođenje naviše
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 ); // Svođenje naviše
}
} /* 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 naviše iz klase
Duvacki na klasu Instrument m ože da ,,suzi“ interfejs, ali o n neće 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 eobično. Z ašto b i iko n a m e rn o zanem ario tip
objekta? Baš to se dešava p ri svo đ en ju naviše. N aizgled je m n o g o logičnije 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 ključnog dela: u
to m slučaju, za svaki tip in s tru m e n ta m o rali biste da pišete n o v u m e to d u m e lo d ija().
P retp o stav im o da tak av način razm išljanja p rim e n ite i n a ostale in stru m en te:

//: polimorfizam/muzika/Muzika2.java
// Preklapanje umesto svođenja naviše
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 svođenja naviše
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 išete p o se b n u m eto d u . To n a sa m o m p o četk u zahteva
više p ro g ram iran ja, ali tak o đ e znači 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 sličnu 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 neće p rijav iti p o ru k u o grešci 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 lepše ako b iste m o g li d a napišete 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 napišete p ro -
g ram tak o d a se ob raća sam o o sn o v n o j klasi?
P olim orfizam v am om ogućava baš to. M eđ u tim , većina 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 teže ra z u m e n ačin n a koji p o lim o rfizam deluje.
Vežba 1: (2) N apravite klasu Cikl s p o tk lasam a Unicikl, Bicikl i Tricikl. Pokažite da se
p rim e ra k svakog o d tih tipova m e to d o m v o z iti( ) m ože svesti naviše na Cikl.

Začkoljica
P roblem s p ro g ram o m Muzika.java m ože d a se u oči 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 zašto 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 uopšte m ože 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 oguće je da za ovaj
te rm in uopšte niste čuli zato što u p ro ce d u ra ln im jezicim a i nije bilo d ru g e m ogućnosti.
P rim era radi, C im a sam o jed an n ačin 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. Pošto prevo-
dilac im a sam o referencu na Instrument, ne m ože da zna koju m eto d u treb a da pozove.
Rešenje tog pro b lem a naziva se kasno vezivanje (engl. late binding), čim e se označava
da se vezivanje obavlja prilik o m izvršavanja, u zavisnosti od tipa objekta. K asno vezivanje
se još naziva i dinam ičko vezivanje (engl. d yn a m ic binding) ili vezivanje prilikom izvrša-
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 izvršavanja 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 -
glašena 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 znači 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 sprečavate redefinisanje. V erovatno da je još b itnije što tim e efektivno
,,isJđjučujete“ d in am ičk o vezivanje, o d n o sn o u k azu jete p rev o d io cu kako d in am ičk o vezi-
vanje nije n e o p h o d n o . To om o g u ćav a nešto efikasnije pozivanje fin aln ih m etoda. U veći-
ni slučajeva, m e đ u tim , tako se neće o stv ariti opšte pob o ljšan 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 poboljšavanje p erfo rm an si.

Dobijanje pravilnog ponašanja


Im ajući 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 pišete tako da se o b raća osn o v n o j Jđasi pa će p o u z d a n o isprav-
no rad iti i sa svim izveđenim klasam a. D ru g im rečim 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“.
Klasičan 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, nažalost, o n pro g ram ere početn ik e m ože navesti da pogrešno pom isle kako O O P služi
sam o za p ro g ra m ira n je grafike, što, n arav n o , nije slučaj.
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
kažem 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 nasleđivanja:

Svođenje naviše
kroz dijagram
nasleđivanja

K v a d ra t Trougao

Identifikator nacrtajd nacrtaj()


objekta Krug obriši obriši
216 Misliti na Javi

Svođenje naviše m ože d a se pojavi čak i u ovako jed n o stav n o j naredbi:

Oblik o = new Krug();

U ov o m slučaju, 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 greška (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 nasleđivanja, Krug jeste Oblik. Stoga se p revodilac slaže s p reth o đ -
n o m n a re d b o m i ne prijavljuje p o ru k u o grešci.
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, očekivati 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 nešto d rugačiji način . N ajpre ćem o n ap rav iti
v išek 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 nasumično 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 zajednički interfejs za sve što je iz nje nasleđeno


- 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 redefinišu ove m e-
tod e da b i obezbedile od g o v araju će p o n ašan je za specifičan oblik.
Klasa GeneratorSlucajnihOblika je neka vrsta ,,proizvođača“ koja vrača referencu na
slučajno 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 svođenje naviše, 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() sadrži 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 išta 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 aša. U to se m ožete u veriti iz rezu ltata p ro g ra m a kada ga
p okren ete.
N asu m ičn o pravljenje o blika treb a da vam dokaže kako prev o d ilac n e m ože zn ati išta
š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 ičk i vezani.
V ežba 2: (1) U p rim e r sa oblicim a d o d ajte ano taciju @ O verriđe.
V ežba 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 redefinišete u izvedenim klasam a. O b jasn ite šta se dešava. Sada je
redefinišite u sam o jed n o j izvedenoj klasi i v idite šta se dešava. K onačno, redefinišite je u
svim izvedenim klasam a.
V ežba 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 ežba 5: (1) U vežbi 1, klasi C ikl dodajte m e to d u to ck o v i() koja vraća broj točkova. 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 zičk 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 , većina m eto d a ili sve naše 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 proširiv; jer m ožete 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 zajedničke 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 dešava 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 nasleđivanja 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 duvačkog"); }
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 žičanog"); }
}

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 računa 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) {
// Svođenje naviše 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 način naštim uje.
Svaki p u t k ada nešto u b acite u niz orkestar u m eto d i m ain(), a u to m atsk i obavljate
svođenje naviše 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 ogućava. 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 utiču. D ru g im rečim a, p o lim o rfiza m je važna te h n ik a koja p ro g ra m e ru o m o -
gućava da „razdvoji o n o što se m e n ja o d o n o g što ostaje isto“.
Vežba 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).
Vežba 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.
Vežba 8: (2) Izm enite d a to te k u Muzika3.java tako da objekte klase Instrument p rav i na
slučajan način kao u dato teci Oblici.java.
Vežba 9: (3) N apravite h ijerarh iju nasleđivanja 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 zajedničke za sve glodare i redefinišite ih u iz-
vedenim klasam a, tako da se različito p o n ašaju 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 događa.
Vežba 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 redefinišite d ru g u m eto d u . N apravite objekat izvedene klase, sve-
dite ga naviše d o njegovog pro sto g tip a i pozovite prv u m etod u. O bjasnite šta se dešava.

Greška: „redefinisanje" privatnih metoda


Evo nečega što biste u blažen o m n ezn an ju m ogli pok u šati d a uradite:

//: pol imorfizam/RedefinisanjePrivatnih.java


// Pokušaj 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 razložno očekivati 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 slučaju 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 ičim neće upo zo riti na to,
ali se pro gram verovatno neće po našati 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 drugačija od im ena p riv atn e m eto d e iz osnovne klase.

Greška: polja i statične metoda


Pošto ste sada naučili šta je p o lim o rfizam , m o gli biste p o m isliti da se sve dešava 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 ičn 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 razrešen u v rem e p rev ođ enja, k ao što pokazu je'
sledeći p rim er:

//: polimorfizam/PristupPolju.java
// Neposredno pristupanje polju biva razrešeno u trenutku prevođenja.

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(); // Svođenje naviše
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 naviše na Nad referencu, sva p ristu p an ja po ljim a razrešava
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 sadrži 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 hoćete Nad polje, m o rate izričito 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
dešava. 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 nećete 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 nećete 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 statična, 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( ) ; // Svođenje naviše
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()
* ///:-

Statične m e to d e su p rid ru ž e n e klasam a, a ne p o jed in ačn im o b jektim a.


224 Misliti na Javi

Konstruktori i polimorfizam
K o n struktori se, kao i obično, razlikuju o d ostalih m etoda. To važi i u slučaju p olim orfizm a.
Iako k o n stru k to ri nisu po lim orfni (to su zapravo statične m etode, ali je njihova deklaracija
statičnosti im plicitna), b itn o je da shvatite način na koji o n i rade u složenim 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 čišćenje i p o -
novo u poglavlju Ponovno korišćenje 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 naviše 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 ičn o p riv a tn i). S am o k o n stru k to r
osno v ne klase im a o dgovarajuće 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 , neće b iti
n apravljen ceo objekat. Z bog toga p revođilac 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 izričito 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 grešku. (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, nasleđivanja 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 , složena 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 nasleđivanja
(č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 znači d a je redosled po-
ziva k o n stru k to ra složenog objekta sledeći:
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 iše 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 nasleđivanja, o osno v n o j klasi
znate sve i m ožete da p ristu p ite svim n jen im jav n im i zaštićenim članovim a. To znači da
m ožete 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 nađete u običnoj 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 nasleđujete već napravljeni. Jedini način 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 ožete 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 ožete, inicijalizujete sve objekte članove (tj. objekte koje u klasu um ećete
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 oštujete, bićete sigurniji da su svi članovi klase i objekti članovi te-
kućeg objekta inicijalizovani. N ažalost, tim e ne p okrivate sve slučajeve, kao što ćete videti
u n are d n o m odeljku.
Vežba 11: (1) D atoteci Sendvic.java d o d ajte klasu KiseliKrastavcic.

Nasleđivanje i čišćenje
K ada n o v u klasu prav ite k o m p o zicijo m i n asledivanjem , uglav n o m nećete m o ra ti da bri-
n ete o ciscenju; p o d o b jek ti se o b ičn o m o g u p rep u stiti sakupljaču sm eća. U koliko čišćenje
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 drugačije). I p rilik o m nasleđivanja, u izvedenoj
klasi m o rate da redefinišete m e to d u ciscenje() ako je p rilik o m saku p ljanja sm eća p o tre b -
n o nekakvo p o seb n o čišćenje. Kada u izvedenoj klasi redefinišete 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, inače o sn o v n a klasa neće
biti očišćena. To dokazuje n a red n i p rim er:

//: polimorfizam/Zaba.java
// Čišćenje i nasleđivanje.
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 - čišće 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("može 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 - č išće 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 rekeće");
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 biće
ZivoBiceO
Pravljenje Karakteristike ima srce
Pravljenje Opisa Z ivo tin ja je , n ije b iljk a
Z ivo tin ja O
Pravljenje Karakteristike može da ž iv i u vodi
Pravljenje Opisa I u vodi i na zemlji
VodozemacO
Pravljenje Karakteristike Krekeće
Pravljenje Opisa Jede bube
Zaba()
Zdravo!
Zaba - čišćenje
čiščenje Opisa Jede bube
čiščenje Karakteristike Krekeće
Vodozemac - čišćenje
čišćenje Opisa I u vodi i na zemlji
čišćenje Karakteristike može da ž ivi u vodi
Zivotinja - čišćenje
čišćenje Opisa Z ivotinja je , n ije b iljk a
čišćenje Karakteristike ima srce
ZivoBice - čišćenje
čišćenje Opisa Elementarno živo biće
čiščenje Karakteristike živo je
* ///:-

Svaka klasa u h ijerarh iji sadrži i objekte članove tipova K a ra k te ristik a i O p is, koji ta-
kode m o raju biti očišćeni. R edosled čišćenja treb a da je su p ro ta n red o sled u inicijalizacije,
za slučaj da je d an p o d o b jek at zavisi od dru g o g . Za polja to znači da redosled čišćenja 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 rateći fo rm u u p o tre b ljen u za d estru k to re u C + + -u )
prv o treba da očistite izvedene klase, a zatim i o sn o v n e klase. R azlog je to što p ri čišćenju
izvedene klase m ožete 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 ištite 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 sledeće: iako ne m o ra te uvek sam i da čistite, o n d a kada m orate,
taj proces zahteva p ažnju i znanje.
V ežba 12: (3) Izm enite vežbu 9 tako da prikazuje ređosled 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 pokažite 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 više d ru g ih objekata p o sed u ju
neki o d tih objekata članova, zad atak p ostaje složeniji, p a više n e m ožete 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 slučaju, 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 ičk o m )
ob jek tu . Evo kako to izgleda:

//: polimorfizam/BrojanjeReferenci.java
// Čišćenje 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("Č išćenje " + 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
čišćenje Kompozicija 0
ćišćenje Kornpozicija 1
čišćenje Kompozicija 2
čišćenje Kompozicija 3
čišćenje Kompozicija 4
čiščenje Deljena 0
* ///.-

Statičan 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 račen je (u
ovom slučaju, to je sam o d o b ra praksa; v ero v atn o n ijed an p rim e r iz ove knjige neće
p ro u zrok o vati prekoračenje takvog brojača). P rom enljiva id je fin a ln a , zato što ne očeku-
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 čišćenje. Ova
teh n ik a zahteva đ o d a ta n tru d , ali ako su objekti koji zahtevaju čišćenje deljeni, nem ate
m n o g o izbora.
Vežba 13: (3) D atoteci BrojanjeReferenci.java d o d ajte m e to d u finalize() da biste p ro-
verili postoji li stanja okončanja (videti poglavlje Inicijalizacija i čišćenje).
Vežba 14: (4) Izm enite vežbu 12 tako da jed an o d objekata ćlanova b u d e đeljen s b ro-
jan jem referenci i pokažite da to ispravno radi.

Ponašanje 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 ičk i p o v ezan u m e to d u objekta čija je kon-
stru k cija u toku?
U n u tar obične m etode, dinam ičk i povezan poziv bio bi razrešen prilikom izvršavanja,
je r objekat ne m ože 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 ički povezanu m eto d u pozovete iz k o n stru k to ra , biće u p o treb ljen a redefi-
nisana verzija te m etode. Ejekat m ože da b u d e p riličn o neočekivan, zato što se redefinisa-
na verzija m etode poziva p re završetka k o n stru isan ja objekta, a to m ože izazvati greške
koje se veom a teško pronalaze.
Po k o ncep tu, posao k o n stru k to ra je da objekat dovede u život (što je priličan podvig).
U n u ta r svakog ko n stru k to ra, ceo objekat m ože biti sam o d elim ičn o fo rm iran - m ožete 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 tekućeg k o n stru k to ra izvedeni delovi jo š n isu inicijalizovani. M ed u tim , d in am ičk i
povezan poziv seže ka „spoljašnjosti11h ijerarh ije nasleđivanja. 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 , pozvaćete 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 ožete d a sagledate u n a re d n o m p rim e ru :

//: polimorfizam/PoliKonstruktori.java
// Konstruktori i polimorfizam
// ne pružaju ono što možda očekujete.
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 ože 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 videćete sle-
deće: 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 tačka ili m o žd a ne bi bilo n ac rta n o ništa, a vi biste ostali zb u n jen i po k u ša-
vajući da otkrijete zašto 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 rešenje m isterije. Proces inicijalizacije zaista teče na sledeči način:
1. P ro sto r za sm eštan 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. Izvršava 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 značila za o d re đ e n i tip) i nije ostavljeno sa slu čajn o zatečen im v re d n o stim a koje
su beskorisne. To uključuje 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-
viće se izuzetak p rilik o m izvršavanja. Sve d ru g o do b ija v re d n o st nula, što je o b ičn o
siguran znak da ćete u re z u lta tu v ideti grešku.
S d ru g e stran e, treb alo bi da vas p riličn o užasne rezu ltat ovog p ro g ra m a . P o stu p ili ste
logično, a ipak je njegovo p o n ašan je tajan stveno pogrešno, a p revodilac se nije b u n io .
(U ovakvim situ a đ ja m a se C + + p o n aša razb o ritije.) O vakve greške 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 alažen 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 ožete, 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 ećete uvek
m oći d a se p rid ržav ate ovog saveta, ali to je ideal kojem tre b a težiti.
V ežba 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 okažite p ro -
blem opisan u ovom odeljku.

Kovarijantni povratni tipovi


Java SE5 je do n ela i kovarijantne povratne tipove, što znači da redefinisana m eto d a izve-
dene Idase m ože vraćati tip izveden iz tip a koji vraća 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 "Pšenica"; }
}
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 PšenicaMl i n ( ) ;
z = m. o b r a d i ();
System.out.println(z);
}
} /* Ispis:
Ži to
Pšenica
* ///:-

K ljučna razlika izm eđu Jave SE5 i ran ijih verzija Jave leži u sledećem : 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 ogućavaiu specifičniji p o v ra tn i tip Psenica.

Projektovanje pomoću nasleđivanja


Kada shvatite polim o rfizam , m ože 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 ože da o p tereti projekat; n aim e, ako o d m a h u p o -
trebite nasleđivanje kada o d postojeće 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 eće uvođenje h ijerarh ije nasledivanja u projekat.
K om pozicija je tak ođe fleksibilnija, je r ste u m o g u ćn o sti da tip o d ab erete d in am ičk i (a
tim e i po n ašan je), do k p ri nasleđivanju m o rate već p rilik o m p rev o đ en ja d a zn ate tačan
tip. To ilustruje n ared n i p rim er:

//: polimorfizam/Kompozicija.java
// Dinamička promena ponašanja objekta
// pomoću 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 sadrži referencu na klasu Glumac koja je inicijalizovana o bjektom


klase SrecanGlumac. To o d re d u je p o n aša n je m eto d e kreni(). Ali p ošto referenca prili-
kom izvršavanja m ože da b u d e povezana s d ru g ačijim ob jek to m , referenca glumac m ože
d a b u d e izm enjena tako da po kazu je na objekat klase TuzanGlumac, čim e se ponašanje
m eto d e kreni() m enja. Z nači, do bili ste d in am ičk u fleksibilnost pri izvršavanju. (O vo se
tak ođe naziva i ob razac State (S tanje). P ogleđajte knjigu T hinking iti Patterns (ivith Java)
na ađresi w w w .M indV icw .net.) N asu p ro t to m e, p riliko m izvršavanja ne m ožete 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 prevođenja.
O p šta sm ern ica je „upotrebljavajte nasleđivanje da izrazite razlike u p o n ašan 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 nasleđene 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 slučaju,
p ro m e n a stanja povlači i p ro m e n u ponašanja.
Vežba 16: (3) Prateći 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 ože da prikaže tri različita 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

Poređenje zamene i proširivanja


Č in i se d a je najbolji p ristu p za pravljenje h ijerarh ije nasleđivanja 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 biće 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 ože da b u d e o zn ačen 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 asleđivanje g a ran tu je da će bilo koja izvedena ldasa im ati
interfejs osn ovn e klase i ništa m anje. Ako p ratite ovaj dijagram , uočićete da izvedene ldase
tak ođ e neće im ati ništa više osim interfejsa o snovne klase.
To m ože 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 ože da p rim i svaku p o ru k u koju m ožete da pošaljete izvedenoj klasi,


jer o n e im aju p o tp u n o isti interfejs. Treba sam o da u ra d ite svođenje naviše iz izvedene
klase pa nikada više nećete m o rati da razm išljate 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 način p o sm atrate, čini se da je p o tp u n a relacija ,,je“ jedini logični na-
čin da takve stvari u rad ite i da je bilo koji dru g i način 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 išljate na taj način , pogledaćete oko sebe i
u stan o v iti da je proširivan je interfejsa (što nažalost rezervisana reč extends p odstiče) sa-
v ršeno rešenje o d re đ e n o g p ro b lem a. To se m ože 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"

Proširivanje
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 rošireni deo interfejsa izvedene klase nije d o stu p a n o sno vn oj klasi, stoga nakon svo-
denja naviše ne m ožete da p ozivate nove m etode:

Obrati se objektu
Korisni deo
klase Koristan Poruka

KoriSugfdeo

Ako u ovom slučaju ne obavljate svođenje naviše, to vas neče opterečivati, ali često
m ožete da d o đ e te u situaciju kada tre b a p o n o v o o tk riti taćan 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.

Svođenje naviše i podaci o tipu prilikom izvršavanja


Pošto prilik o m svođenja naviše (k reta n ja uz h ije rarh iju nasleđivanja) 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 asleđivanja - u p o treb ljav ate svodenje naniže (engl. downcast).
N aučili ste da je svođenje naviše uvek sig u rn o , je r osn o v n a klasa ne m ože da im a širi in-
terfejs o d izvedene Idase, pa će stoga svaka p o ru k a koju pošaljete preko interfejsa osn ov ne
klase biti prihvaćena. Ali p rilik o m svođenja naniže 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 nešto drugo.
Poglavlje 8: Polimorfizam 237

D a b i se ovaj p ro b le m rešio, m o ra d a p o sto ji n a čin za g aran to v an je ispravnog svođenja


nan iže, kako ne biste greškom u rad ili svođenje u p o g rešan tip i zatim poslali p o ru k u koju
objekat n e m ože 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 svođenje naniže. M eđ u tim , u Javi se p roveravaju sva svođenjal Iako naiz-
gled obavljate o b ič n u konverziju, p rilik o m izvršavanja se u tv rđ u je da li je to zaista onaj
tip koji vi m islite d a jeste. Ako to nije slučaj, 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 izvršavanja naziva se p o d a c i o tip o v im a p rilik o m izvrša-
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


// Svođenje naniže i podaci o tipovima prilikom izvršavanja (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 prevođenja: metoda nije pronađena u klasi Koristan
//! x [1] .u ();
( (Korisniji)x [1]).u (); // Svođenje naniže/RTTI
((Korisniji)x [0]).u (); // biće bačen izuzetak
}
} ///:-

Kao što se viđi s p re th o d n o g dijagram a, klasa Korisniji proširuje interfejs klase Kori-
stan. Pošto je nasleđena, o n a takođe m ože da b u d e svedena naviše ka klasi Koristan. To
m ožete da viđite u okviru inicijalizacije niza x u m etodi main(). Pošto oba objekta u nizu
pripad aju klasi Koristan, za oba m ožete da pozovete m etode f() i g(), a ako pokušate da p o -
zovete m eto d u u() (koja postoji sam o u klasi Korisniji), dobićete grešku p ri prevođenju.
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 naniže. Ako je tip odgovarajući, o n o će b iti uspešno. U su p ro tn o m , dobićete 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 pišete poseban
kod, jer o n naznačava p ro g ram ersk u grešku 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 prevođenje i
pakovanje (engl. build) p rim era iz ove knjige kako očekuje da će ovaj p ro g ra m prilikom
izvršavanja generisati izuzetak.
RTTI je više o d o bične konverzije. Postoji, na p rim er, način da u stanovite tip pre nego što
po kušate da obavite njegovo svođenje naniže. K om pletno poglavlje Podaci o tipovim a p o-
svećeno je proučav an ju različitih vidova po d atak a o tip o v im a prilik o m izvršavanja u Javi.
V ežba 17: (2) K oristeći 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 naviše
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 naniže, pozovite m e to d u ra v n o te ž a () i p o g led ajte šta se dešava.

Sažetak
Po lim o rfizam znači „različiti 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 različite oblike koji u p o treb ljav aju taj interfejs: različite verzije
d in am ičk i pov ezan ih m etoda.
U ov o m poglavlju videli ste da nije m oguće 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 nasleđivanja. P o lim o rfizam je o so b in a koja ne m ože d a se
p o sm a tra izolovano (kao što m ože, n a p rim er, n ared b a svvitch), već sam o kao deo „veće
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 zajedničke o so b in e m e đ u klasam a kao i njihove
m eđ u so b n e relacije. Iako to iziskuje značajan n ap o r, o n se i te kako isplati. Postiže se brži
razvoj p ro g ra m a, bolja organizacija koda, pro širiv i p ro g ra m i i lakše od ržav an je koda.
Rešenja odabranih vežbi data su u elektronskom dokum entu Thinking in Java Annotatcd Solution
Guide, koji se može kupiti na lokaciji www.MindView.net.
Interfejsi
Interfejsi i apstraktne klase pružaju strukturiraniji načiti 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 ršk 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 ršk a kroz
rezervisane reči.
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 ičn e
klase i interfejsa. Iako ćete biti skloniji p ravljenju interfejsa, a p stra k tn a klasa je važna 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 ,,lažne“ 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 zajedničkog interfejsa jeste da se o m o g u ći
različito p onašanje svakog p o jed in ačn o g p o d tip a . O n u spostavlja o sn o v n i izgled, tak o da
znate šta je zajedničko za sve izvedene klase. D ru g i način da to iskažete 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 značenje. A p strak tn u klasu prav ite kada želite d a s g ru p o m klasa rad ite p re-
ko zajedničkog interfejsa. Tako In s tru m e n t služi sam o da se opiše 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 poželeti da
sprečite 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 grešci, ali tim e in fo rm ac iju odlažete sve do
izvršavanja i od korisnika zahtevate p riličn o tem eljn o testiran je. Uvek je bolje prijav iti
p ro b lem e u vrem e prevođenja.
Java za to obezbeđuje 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
sledeća:

abstract void f ();

Klasa koja sadrži ap strak tn e m eto d e naziva se apstraktna klasa. Ako kJasa sadrži je d n u
ili više 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 grešku.)
Pošto 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-
kuša da napravi objekat takve klase? Pošto n e m ože da n ap rav i objek at a p strak tn e klase na
siguran način, prevodilac će prijaviti grešku. Na ovaj način 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 rešn 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 označite rezerv isano m reči 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 sadrži 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 sprečite 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 ože 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 računa 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) {


// Svođenje naviše 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 izričito o d re đ u je ap stra k tn o st klase
i objašnjava n je n u n a m e n u i k o risn ik u i p rev o d io cu . Takođe, 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 zajedničkih
m eto d a naviše u hijerarh iji nasleđivanja.
Vežba 1: (1) Izm enite vežbu 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.
Vežba 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 uće n ap rav iti in stan ce te klase.
Vežba 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 eđefinisana 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
različitu 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.
Vežba4: (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 statičn 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 iže 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 inišite p o treb u
za svođenjem naniže.

Interfejsi
Rezervisana reč interface u n a p re đ u je k o n cep t apstrakcije. R ezervisana reč abstract o m o -
gućava da u klasi n ap rav ite je d n u ili više 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 značava 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 definiše 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 obezbeđuje 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 određeni interfejs zna koje m etode interfejsa m ože da koristi i to je
sve. Interfejs se, znači, koristi d a ustan o v i „k o m unikacioni p ro to k o l“ izm eđu 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 „višestrukog nasleđivanja“ C + + -a, je r p o m o ć u njega
m ožete d a n ap rav ite klasu koja se m o že svesti naviše p rem a više o sn o v n ih tipova.
D a biste n ap ravili interfejs, u m esto rezervisane reči class u p o treb ite rezervisanu reč
interface. Kao i k od klasa, ispred rezervisane reči 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 ožete da upotrebljavate sam o
u n u ta r istog paketa. Interfejs m ože d a sadrži i polja, ali su o n a im p licitn o (auto m atski)
defin isan a kao statičn 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
nasleđivanja. Izloženi 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 ožete videti sledeće: kada realizujete in-


terfejs, ta realizacija postaje običn a klasa k oja m ože d a se p ro širu je na uo bičajene načine.
D eklaracije m eto d a u interfejsu m ožete eksplicitno d a pro glasite jav n im . O n e su, m e-
đ u tim , javne čak i ako to ne učinite. 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 proglašene 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 nasleđivanja, što
Java prevodilac ne dozvoljava.
To m ožete 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 neće d o p u stiti n išta 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 prevođenju:
int IZNOS = 5; // statična i finalna
// Ne može 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 računa 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) {
// Svođenje naviše 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 učinjena 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 način. Pošto 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 svođenje naviše ka obič-
noj klasi Instrument, a p strak tn o j klasi Instrument ili interfejsu Instrument. P onašanje
je id en tičn 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.
Vežba 5: (2) U zasebn o m pak etu n ap rav ite interfejs koji sadrži tri m etode. R ealizujte taj
interfejs u nekom d ru g o m paketu.
Vežba 6: (2) D okažite da su sve m eto d e interfejsa au to m a tsk i javne.
246 Misliti na Javi

Vežba 7: ( 1) Izm enite vežbu 9 iz pogiavlja Polim orfizam tak o da klasu Glodar p re tv o rite u
interfejs.
Vežba 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.
Vežba9: (3) Izm enite p ro g ram Muzika5.java praveći a p stra k tn u klasu koja sadrži zajed-
ničke m eto d e po tklasa Duvacki, Udaraljke i Zicani.
Vežba 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 acujući ga u listu iza rezervisane reči 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 ićen 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 sreće. Interfejs z n a tn o slabi to ograničenje. Stoga m ožete da piše-
te kod koji je lakše u p o tre b iti više 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 više različitih v rsta Procesora. U ovom slučaju, p o d tip o v i Procesora m odifi-
kuju String objekte (vodite raču 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 vraćanje
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 graničnik 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("Uključen 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:
Uključen procesor VelikimSlovima
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Uključen procesor MalimSlovima
disagreement with beliefs is by definition incorrect
Uključen 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 aša različito, u zavisnosti o d arg u -
m en ta objekta koji joj je p rosleđen, naziva se p ro jektn i o b razac Strategy (strategija). Ta
m eto d a sadrži n ep ro m en ljiv deo a lg o ritm a koji treba izvršiti, d o k p ro m en ljiv deo sadrži
S trategija. S trategija je objekat koji prosledujete; o n sadrži kod koji treb a izvršiti. O vde je
S trategija objekat tipa Procesor, a u m etodi main() vidite tri različite 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 koristeči svoj ar-
g u m e n t kao g raničn ik, a vraća String[], O vde je u p o treb ljen a za b rže pravljenje niza ob-
jekata tipa String.
P retp ostavim o da ste pronašli 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; // Lažna 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 pošto 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 ožete u p o tre b iti s m e to d o m Primeni.obradi(), iako bi to bilo lepo. U suštini, veza iz-
m ed u Primeni.obradi() i Procesora jača je nego što je p o tre b n o , a to sprečava 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 ažn ju na to da
su i ulazi i izlazi tip a Talasnioblik.
D a je Procesor interfejs, o g ra n ičen 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(''Uključen procesor " + p.ime());
print(p.obradi (s));
}
} ///= -

Prvi način 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 išu svoje klase tako d a b u d u usaglašene 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 vraćanje
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:
Uključen procesor VelikimSlovima
IF SHE UEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD
Uključen procesor MalimSlovima
if she weighs the same as a duck, she's made of wood
Uključen procesor Delilac
[If, she, weighs, the, same, as, a, duck,, she's, made, of, wood]
* ///:-

M eđ u tim , često n e m ožete 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
slučajevim 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 način
kako u A d ap teru pišete k o d koji če preuzeti postojeći 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:
Uključen procesor Niskopropusni
Talasnioblik 0
Uključen procesor Visokopropusni
Talasnioblik 0
Uključen 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 ožda ste
uočili 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
više različitih reaiizacija, čim e k o d postaje lakše p o n o v o upotrebljiv.
Vežba 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().

Višestruko nasleđivanje u Javi


Pošto interfejs uopšte nem a nikakvu realizaciju - o d n o sn o za njega se ne odvaja nikakvo
m esto za skladištenje - ništa nas ne sprečava da k o m b in u je m o više interfejsa. To je k o ri-
sno, jer p o n ek ad treba reći da je ,,x tip a i a i b i c “. O vo k o m b in o v an je interfejsa više klasa,
u C + + -u se naziva višestruko nasleđivatije i sa so b o m nosi p riličn o o b im a n prtljag, je r sva-
ka klasa m ože da im a različitu realizaciju neke m eto de. U Javi m ožete da u rad ite isto, ali
realizaciju m ože 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 više 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 asleđujete nešto što nije interfejs, m ožete 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 reči implements.
252 Misliti na Javi

M ožete da n avedete koliko g o d želite interfejsa - svaki po staje nezavisan tip, ka kojem
m o žete da svedete naviše 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
// Više 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 način 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 grešku.)
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 ožete 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 izričito 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 oguče 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 ože d a se p ro sledi bilo kojoj o d tih m eto d a,
što znači da se m ože svesti naviše ka svakom o d tih interfejsa. To se obavlja b ez p rep rek a
i bez p o se b n o g angažovanja p ro g ram era, zahvaljujući n ačin 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 suštinski razlog p o sto jan ja interfejsa:
m o g u č n o st da se obavi svođenje naviše ka više o sn o v n ih tipova. M eđ u tim , d ru g i razlog za
koriščenje interfeisa isti je kao i za korišćenje 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 ožete 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 suštini, ako znate da će nešto b iti osn ov na klasa, p rv o p o -
kušajte d a to napravite kao interfejs. (O to m e ćem o još govoriti u sažetku ovog poglavlja.)
Vežba 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.
Vežba 13: (2) N apravite interfejs koji nasleđuju dva d ru g a interfejsa. N eka treći interfejs
nasledi ta dva.:

Proširivanje interfejsa nasleđivanjem


P o m o ću nasieđivanja, 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 slučaja do bićete n o v interfejs, kao što
se vidi iz n ared n o g prim era :

//: i nterfejsi/HororPredstava.java
// Proširivanje interfejsa nasleđivanjem.

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 OpasnoCuđoviste {


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 ože 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 pošto in-
terfejs m o že da se sastoji od više d ru g ih , p rilik o m pravljenja novog interfejsa rezervisana
reč extends m ože da se o d n o si na više interfejsa. Kao što vidite, im ena interfejsa sam o su
razdvojena zarezim a.
Vežba 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 realizujući novofor-
m ira n i interfejs i isto v rem en o nasleđujući k o n k retn u klasu. Sada napišite č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.
Vežba 15: (2) Izm en ite p re th o d n u vežbu praveći 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 više interfejsa m ožete d a se suočite s m alo m začkoljicom . 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 ašem 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 {


// Identična, 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 ešani, 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 . Kađa se u kloni
k o m e n ta r ispred poslednja dva reda p rim e ra , p o ru k a o grešci 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 različitim 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

Prilagođavanje interfejsu
Jedan o d najvažnijih razloga za u p o tre b u in terfejsa jeste m o g u ć n o st p o sto jan ja više rea-
lizacija istog interfejsa. U je d n o stav n im slučajevim 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 ičn 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 apišete 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 suštin i, vi kažete: „M ožeš 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 usaglašen s m o jim interfejsom ". T im e vaša m e to d a po staje fleksibil-
nija, opštija i upotrebljivija.
N a prim er, k o n stru k to r Java SE5 klase Scanner (o kojoj ćete više sazn ati u poglavlju
Z n akovn i nizovi) p rim a interfejs Readable. V idećete 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 ograniči svoj a rg u m e n t n a o d re đ e n u klasu. N a taj način , Scanner
m ože da radi s više 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 usaglašen sa određenom 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 načina; v ideti
d o k u m e n ta c iju klase CharBuffer) ili vraćate -1 kada ulaza više nem a.
P retp o stav im o da im ate klasu koja nije realizovala Readable - kako ćete postići da o n a
radi s klasom Scanner? Evo jed n e klase koja pro izv o d i slučajne 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 slučaju prilag o đ en a
klasa m ože nap rav iti nasleđivanjem i realizacijom interfejsa R ead ab le. D akle, koristeći
pseu do višestruk o nasledivanje koje o m ogućava 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 nasleđivanjem.
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
* ///:-

Pošto na ovaj način svakoj postojećoj klasi m o žete d o d a ti interfejs, znači da m etoda
koja p rim a interfejs obezbeđuje i n ačin p rilag o đ en ja bilo koje klase za rad s to m m eto -
dom . U to m e leži m oć korišćenja interfejsa u m esto klasa.
V ežba 16: (3) N apravite klasu koja proizvodi niz znakova (objekata tip a ch a r). Prilagodite
tu klasu tako da se m ože u p o tre b iti za ulaz objekta tip a S can n er.

Polja u interfejsima
Pošto su sva polja koja stavite u interfejs au to m atsk i statičn 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 način da se
postig ne efekat koji daje rezervisana reč e n u m u C -u ili C ++-U . D akle, naići ć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 ažn ju na to da je i ovde u p o treb ljen uob ičajen i sti! pisanja statičn ih finalnih
polja u Javi, čije su v red n o sti inicijalizovane k o n stan ta m a —sve se piše velikim slovim a,
Poglavlje 9: Interfejsi 259

d o k se više reči 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 više 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 sadrže 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). Više p o jed in o sti o u p o tre b i rezervisane reči e n u m naći ćete u poglavlju
N abrojani tipovi.
V ežba 17: (2) D okažite da su p olja interfejsa au to m atsk i statičn 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 pomoću
// 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;
) ///:-

Pošto su polja statičn a, biće 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 tičn o m skladištu to g interfejsa.


260 Misliti na Javi

Ugnežđivanje interfejsa
Interfejsi3 m o g u da b u d u u g n ežden 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

// Suvišan “public":
public interface H {
void f();
}
void g ( ) ;
// Ne može 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 može 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( );
// Vraća samo A.D:
//! A.DImp2 d i2 = a.uzmiD();
// Ne može da se pristupi članu interfejsa:
//! a. u z m i D ( ) .f ();
// Samo drugi A može nešto da uradi sa uzmiD():
A a2 = new A ( ) ;
a2 .p ri mi D( a. uz mi D( ));
}
} ///:-
S intaksa ugnežđivanja interfejsa u n u ta r klase p riličn o je očig 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 začkoljica: interfejsi takođe m ogu da b u d u p rivatn i, kao što je slučaj s A.D
(za ugnežđene interfejse koristi se ista sintaksa kao i za u gnežđene klase). Č em u služi p ri-
262 Misliti na Javi

vatni ugnežđeni interfejs? M ogli biste p retp o stav iti kako o n m ože sam o da b u d e realizovan
kao priv atn a ugnežđena klasa u klasi D lm p, ali n am klasa A.DImp2 pokazuje d a o n takođe
m ože biti realizovan i kao jav n a klasa. Klasa A.DImp2, m ed u tim , m ože da se up otrebljava
sam o kao klasa tog o d ređ en o g tipa. Pošto ne p ostoji način da se spolja vidi kako ona, u
stvari, realizuje privatn i interfejs D, to je realizovanje p riv atn og interfejsa način 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 nećete dozvoliti svođenje naviše).
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 vraća referencu n a p riv a tn i interfejs. Šta m ožete 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 uočite 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 završavaju n eu sp eh o m . P o v ratn u v red n o st jed in o
m ožete d a p red ate d ru g o m o b jek tu koji im a ovlašćenja da je koristi - u ovom slučaju,
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 ežđen 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 ože b iti proglašen p riv atn im .
Klasa Ugnezdjivanjelnterfejsa p o k azu je različite načine na koje ugnežđeni 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 gnežđeni. Takođe, p riv atn i interfejsi ne m o gu da b u d u realizovani
van klase u kojoj su definisani.
U početk u vam se m ože 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 ožete naići na
m esto na kom e će k o risn o d a vam posluži.

Interfejsi i Proizvođači
Interfejs bi trebalo da posluži kao p u t do više realizacija, a tipičan način pravljenja objekata
usklađenih 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 ogućilo 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 tačan tip in-
terfejsa U slu g a koji se prav i, da bi on m ogao da pozove odgovarajući k on stru ktor.
Z ašto da čovek do d aje još jed an nivo posredovanja? Jedan od uobičajenih razloga je
pravljenje zajedničkog 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 pomoću 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 složen deo k o d a, ovaj p ristu p om og ućav a p o n o v n u


u p o tre b u tog koda s različitim v rstam a igara. P ostoje i kom plik ov anije igre za koje bi se
m ogao iskoristiti taj obrazac.
U sledečem poglavlju videćete elegan tniji n ačin za realizovanje Proizvođača p o m o ć u
a n o n im n ih u n u tra šn jih klasa.
Vežba 18: (2) N apravite interfejs Cikl s realizacijam a Unicikl, Bicikl i Tricikl. N apravite
Proizvođače za svaki tip Cikla i k o d koji u p o treb ljav a te Proizvođače.
Vežba 19: (3) P o m o ću p ro izv o d n ih m eto d a n ap rav ite zajednički k o stu r za bacanje n o -
včića i bacanje kocke.

Sažetak
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 ože n a p ra -
viti interfejs i Proizvođač.
M no gi su podlegli to m iskušenju i n apravili interfejse i Proizvođače gde go d su m ogli.
Razlog: m o žd a će zatrebati dru g ačija 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 otiviše 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 povećavate složenost. D o d atn a
složenost 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. Počnite 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 lične 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.
Unutrašnje kfase
D efiniciju klase m ožete 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 lišete n jih o v u m e đ u so b n u viđljivost. Važno je shvatiti da su u n u tra šn je klase b itn o
različite 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 aučičete d a u n u tra šn ja klasa p ru -
ža i više o d toga - o n a je svesna p risustv a klase koja je o k ru žu je i m ože s n jo m d a k o m u -
n icira. Iako su za većin u u n u tra šn je klase sasvim n o v k on cep t, p o m o ć u n jih m ožete da
pišete finiji i jasn iji ko d , m a d a niko n e jam či d a će takav i biti.
D o k učite o u n u tra šn jim klasam a, n jihova sv rh a često nije očigledna, pa navikavanje
n a njih m ože 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 ašto u n u tra šn je klase?“ naći ć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 neće treb ati, na-
ro čito ne isprva. Z ato će vam p o četn i delovi poglavlja zasađ verovatno biti dovoljni, a de-
taljnije istraživanje m ožete sm a tra ti referen tnim m aterijalo m .

Pravljenje unutrašnjih klasa


U n u tra šn ju klasu prav ite baš kako biste očekivali - tako što definiciju klase u m etn ete u
sp o ljn u klasu:

//: unutrasnjeklase/Posi1j k a l .java


// Pravljenje unutrašnjih 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 unutrašnjih klasa izgleda kao i upotreba
// bilo koje druge klase unutar klase Posiljkal:
public void posalji(String odr) {
Sadrzina c = new Sadrzina();
Poglavlje 10: Unutrašnje 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 slučaju, jed in a važna 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 ćešće im ati m e to d u koja v raća 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
// Vraćanje reference na unutrašnju 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 unutrašnje 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 estatičn e m eto d e spoljne
klase, kao tip to g objekta m o rate d a navedete Im eSpoljneKlase.ImeU nutrašnjeKlase, kao
što se vid i u m e to d i m a in ().
Vežba I: (1) N apišite klasu Spoljna koja sadrži u n u tra š n ju klasu Unutrasnja. Klasi Spolj-
na d o d ajte m e to d u koja vraća 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 začkoljica. 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 trašn 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, ugnežđene 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: Unutrašnje 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 ožete 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 tražen 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 više 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 tekući obje-
k at (m e to d a tekuci()) i da se p o m erite na sledeći elem en t u n izu (m eto d a sledeci()).
Pošto 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 ože biti arg u m en t m n o g ih m eto d a, čim e se prav i generički kod.
U našem p rim e ru , p riv a tn a klasa SelektorSekvence pruža 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 pažljivije. O b ratite p ažn ju na to da se sve m eto d e - kraj(), tekuci() i
sledeci() —o b raćaju 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 trašn ja klasa, m e đ u tim , m ože 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 ože 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 nači, u n u tra šn ja klasa im a au to m atsk i p ristu p članovim a klase koja je okružuje.
Kako se to dešava? U n u trašn 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 sreću, b rin e o svim d etaljim a um esto
vas. Sada k o n ačn o m ožete da shvatite zašto u n u tra šn ja klasa m ože 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 estatičn 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 ože da pristu p i. U većini slučajeva, ovaj
scenario se odigrava bez intervencije p ro g ram era.
Vežba 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 prikažite ih.
Vežba 3: (1) Izm en ite vežbu 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 ažite ga.
270 Misliti na Javi

Upotreba sintaksi .this i .new


Ako v am u u n u trašn jo j klasi treb a referenca n a objek at spoljne klase, n avedite im e spoljne
klase i iza nje tačk 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 izvršavanja, p a nem a
režijskih troškova 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 trašn 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 unutrašnje 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 očekivanju, da biste d ire k tn o n apravili objekat u n u tra šn je klase ne treba da


p ratite uob ičajenu 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
rešavaju i p itan ja oblasti važenja za u n u tra šn ju klasu, pa se ne kaže dn.nevv TackaN o-
v a .U n u tr a s n ja ( ). (To se čak ne m ože reći.)
Poglavlje 10: Unutrašnje klase 271

Z nači, ako n em ate o b jek at spo ljn e klase, ne m ožete 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 ugtiežđenu 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 Pošiljka:

//: unutrasnjeklase/Posiljka3.java
// Pravljenje instanci unutrašnje 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 unutrašnje klase:
Posiljka3.Sadrzina c = p.new Sadrzina();
Posiljka3.0drediste d = p.new Odrediste("Tanzanija“) ;
}
} ///:-

V ežb 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.
Vežba 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.

Unutrašnje klase i svođenje naviše


U n utrašn je klase dolaze d o izražaja kada počnete da prim enjujete svođenje naviše ka osnov-
noj klasi, a posebno ka interfejsu. (Efekat dobijan ja reference na interfejs o d objekta koji ga
realizuje, u suštini je isti kao i svođenje naviše ka osnovnoj klasi.) O vo biva zato što u n u tra-
šnja klasa koja realizuje interfejs m ože biti p o tp u n o nevidljiva i n edo stup na, što je pogodno
za sakrivanje realizacije. Vraća vam se sam o referenca na osn o vn u klasu ili interfejs.
Prvo ćem o definisati zajedničke 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 našu 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 sledećem 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 možete da pristupite privatnoj klasi:
//! Posiljka4.PSadrzina pc = p.new PSadrzina();
}
} III---
U klasi Posiljka4 d o d ato je nešto novo: u n u trašn ja klasa P S ad rzln a je privatna tako da
niko osim klase P osiljka4 ne m ože da joj pristupi. O bičn e klase (koje nisu u n u trašn je) ne
m ogu da b u d u privatne niti zaštićene - m ogu im ati sam o javni ili paketni pristup. Klasa
P O d re d iste je zaštićena, pa ne m ože da joj p ristu p a niko osim klase P osiljka4, klasa koje su
u istom paketu kao i P osiljka4 (pošto rezervisana reč p ro te c te d takođe dozvoljava paketni
pristu p ) i naslednika klase P osiljka4. To znači da p ro g ra m e r klijent ne m ože p o tp u n o da
Poglavlje 10: Unutrašnje klase 273

poznaje te ćlanove, niti m ože d a im p ristu p a bez ograničenja. Ka priv atn oj u n u trašn jo j klasi
(ili ka zaštićenoj u n u tra šn jo j klasi ako niste neki o d naslednika), u stvari, ne m ožete d a oba-
vite n i svođenje naniže, je r ne m ožete 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 trašn je klase stoga o m ogućavaju p ro jek tan tu klase da p o tp u -
no spreči zavisnost o d tip a i da p o tp u n o sakrije p o d atk e o realizaciji. Pored toga, proširiva-
nje interfejsa je beskorisno, iz perspektive p ro g ra m e ra klijenta, je r o n ne m ože da p ristu p i
ostalim m eto d am a koje nisu deo javnog interfejsa. T im e se takođe Javinom prevodiocu
om ogućava da pravi efikasniji kod.
V ežba 6: (2) N apravite interfejs koji sadrži b arem je d n u m e to d u u so pstv eno m paketu.
D o dajte zaštićenu u n u tra šn ju Jdasu koja realizuje taj interfejs. U trećem p ak etu nasledite
svoju kJasu i, u n u ta r m eto d e, v ratite o bjek at zaštićene u n u tra šn je kJase, svodeći naviše na
interfejs to k o m vraćanja.
V ežba 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 ažite dejstvo na objekat spo ljn e kJase.
V ežba 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.

Unutrašnje klase u metodama i oblastima važenja


Na osnovu o noga što ste dosad videli, stekli ste p red stav u o uobičajenoj 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 trašn je klase, po
pravilu biće sa „običnim " 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 više d rug ih , skrivenih načina
kako da ih, ako želite, koristite: u n u trašn je kJase m o g u da b u d u napravljene u n u ta r m etoda
ili čak u proizvoljnoj oblasti važenja. Tako nešto m ožete da u rad ite iz dva razloga, ukoliko:
1. Kao što je ranije p okazano, realizujete interfejs tak o da m ožete da n ap rav ite i v ratite
referencu.
2. Rešavate složen p ro b lem i želite da n ap rav ite klasu koja će vam p o m o ći u njego-
vom rešavanju, 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 važenja 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 važenja je d n a m eto d a (um esto
da je oblast važenja d ru g a klasa). To je lokalna unutrašnja klasa:

//: unutrasnjeklase/Posiljka5.java
// Ugnežđivanje 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 svođenje naviše
u n ared b i return - m eto d a odr() vraća 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 znači da
o bjekti klase POdrediste n akon p o v ratk a iz te m eto d e neće biti ispravni.
O b ratite p ažnju na to da ste za im ena u n u trašn 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 ožete da u g nezdite u n u ta r proiz-
voljne oblasti važenja:

//: unutrasnjeklase/Posi1jka6.java
// Ugnežđivanje klase unutar oblasti važenja

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: Unutrašnje klase 275

// Ovde ne možete da je koristite! Izvan je oblasti važenja:


//! 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 važenja n a re d b e if. To n e znači
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 važenja u n u ta r koje je definisana. O sim toga, ista je kao svaka
d ru g a o b ičn a klasa.
Vežba 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 definišući
u n u tra šn ju klasu u n u ta r neke m eto d e koja vraća referencu n a vaš interfejs.
Vežba 10: (1) P onovite p re th o d n u vežbu, ali definišite u n u tra šn ju klasu u nekoj oblasti
važenja u o k v iru m etode.
Vežba 11: (2) N ap rav ite p riv atn u u n u tra šn ju klasu koja realizuje jav n i interfejs. N apišite
m e to d u koja vraća referencu n a in stan cu p riv atn e u n u tra šn je klase, svedenu naviše ka in-
terfejsu. Pokažite 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 iže ka njoj.

Anonimne unutrašnje klase


N are d n i p rim e r izgleda p o m alo ču d n o :

/ / : unutrasnjeklase/Posiljka7.java
// Metoda koja vraća anonimnu unutrašnju klasu.

public class Posiljka7 {


public Sadrzina sadr() {
return new Sadrzina() { // Umetanje definicije klase
private int i = 11;
public int vrednost() { return i; }
}; // U ovom slučaju je potrebna tačka 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 počin jete da pravite n o v objek at klase Sadrzina, ali tad a, p re nego
što d o d e te do tačke i zareza, kažete: „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 znači: „N ap rav i o b jek at a n o n im n e klase koja nasleđuje ldasu Sa-


drzina“ R eferenca koju vraća o p e ra to r new a u to m a tsk i se svodi naviše na referen cu klase
Sadrzina. Sintaksa za a n o n im n u u n u tra š n ju klasu je sk raćeni oblik za:

//: unutrasnjeklase/Posiljka7b.java
// Proširena 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 trašn 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 vaša osn o v n a klasa zahteva k o n stru k ciju
sa arg u m en to m :

//: unutrasnjeklase/Posi1jka8.java
// Anonimna unutrašnja klasa koja poziva konstruktor osnovne klase.

public class Po si1j ka8 {


public Omotac omot(int x) {
// Poziv konstruktoru osnovne klase:
return new Omotac(x) { // Prosleđivanje argumenta konstruktoru.
public int vrednost() {
return super.vrednost() * 47;
}
}; // Potrebna je tačka 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 obična
klasa s realizacijom , o n a se u p o treb ljav a i kao zajednički interfejs za svoje izvedene klase:

//: unutrasnjeklase/Omotac.java
public class Omotac {
private int i;
Poglavlje 10: Unutrašnje 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 tačka i zarez n a k raju a n o n im n e u n u tra šn je klase n e označava kraj tela klase. O n
označava kraj izraza koji u d ato m slučaju sadrži i a n o n im n u klasu. D akle, u p o tre b a tačke
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 definišete
polja:

//: unutrasnjeklase/Posi1jka9.java
// Anonimna unutrašnja klasa koja obavlja inicijalizaciju.
// Kraća verzija datoteke Posiljka5.java.

public class Posiljka9 {


// Argument koji koristite u anonimnoj unutrašnjoj
// 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 definišete 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 obićete grešku p rilikom prevođenja.
D okle god sam o dodeljujete vrednosti poljim a, gornji p ristu p je u redu. Ali šta ako treba
da obavite neku aktivnost sličnu k o n struktoru? Pošto je klasa an o n im n a, k o n stru k to r u njoj
ne m ože da im a im e (jer o n o ne postoji!). Na sledeći način m ožete efektivno da napravite
k o n stru k to r za a n o n im n u u n u trašn ju klasu p o m o ću bloka za itiicijalizaciju instancv.

I I : unutrasnjeklase/KonstruktorAnonimne.java
// Pravljenje konstruktora za anonimnu unutrašnju 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 slučaju, p ro m en ljiv a i nije m o rala d a b u d e finalna. lako se i p rosleđuje 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 naša Pošiljka 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 unutrašnje 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("Prekoračen 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:
Prekoračen budžet!
* ///:-
Poglavjje 10: Unutrašnje klase 27 9

U n u ta r b loka za inicijalizaciju in s ta n đ uočavate kod koji ne m ože 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 ičen ja; b lo k za inicijalizaciju in stan ci ne m ožete 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 nešto o g raničenije nego o bično nasleđivanje, p ošto
o ne m o g u da p ro šire klasu ili d a realizuju interfejs, ali ne oboje. A ako realizujete interfejs,
m ožete da realizujete sam o jed an .
Vežba 12: (1) P onovite vežbu 7 k oristeći a n o n im n u u n u tra šn ju klasu.
Vežba 13: (1) P onovite vežbu 9 koristeći a n o n im n u u n u tra šn ju klasu.
Vežba 14: (1) P repravite p ro g ram interfejsi/HororPredstava.java tako da interfejse
OpasnoCuđoviste i Vampir realizujete p o m o ć u a n o n im n ih klasa.
Vežba 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 vraća referencu na prv u klasu. N apravite
objekat koji ćete v ratiti koristeći an o n im n u u n u tra šn ju klasu koja nasleđuje p rv u klasu.

Ponovo o proizvodnim metodama


P ogledajte koliko lepše 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 međusobno 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 statičn o polje u realizaciji interfejsa
Usluga. D obija se i sm islenija sintaksa.
I p rim e r interfejsi/Igre.java m ože biti poboljšan 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 pomoču unutrašnjih 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 ežba 16: (1) P repravite rešenje vežbe 18 iz po^lavlj; ■»risteći a n o n im n u u n u -
tra šn ju klasu.
V ežba 17: (1) P repravite rešenje vežbe i 'j iz poj-ia • ;oristeći a n o n im n u u n u -
tra š n ju klasu.
282 Misliti na Javi

Ugnežđene klase
A ko vam n e treb a veza izm eđu n jen ih o b jek ata i o b jek ata sp oljn e klase, u n u tra šn ju klasu
m ožete da proglasite statičn o m . O n a se o b ičn o naziva ugnežđena klasa2. D a biste shvatili
značenje 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 kažete da je u n u tra šn ja klasa statičn a, ovo ne važi. U gnežđena klasa znači 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 gnežđene 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 nestatičn 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 estatičn a u n u tra šn ja klasa ne m o že d a im a statič n e p o d atk e , statična
polja niti statičn e u n u tra šn je klase. M eđ u tim , u g n e žđ e n e (statičn e u n u tra šn je ) klase
m o g u sve to da sadrže:

//: unutrasnjeklase/Posiljkall.java
// Ugnežđene klase (statične unutrašnje 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; }
// Statična unutrašnja klasa može da sadrži druge statične 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 slično 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: Unutrašnje 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 raćaju 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 ičn o j (n estatičn 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 tatičn 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.
Vežba 18: (1) N ap rav ite k lasu koja sađrži 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.
Vežba 19: (2) N ap rav ite klasu koja sađrži u n u tra š n ju klasu koja i sam a sadrži u n u tra šn ju
klasu. N astavite dalje ovako, ko risteći 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 ožete d a pišete bilo kakav ko d , ali statičn a u n u tra šn ja klasa m ože 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 statična. Pošto je klasa statičn a, o n a ne n aru šav a p rav ila o interfejsim a - statična
u n u tra šn ja klasa se sam o u m eće 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 način:

//: 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 zajednički k o d koji


će b iti korišćen sa svim različitim 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 količina p re v ed en o g koda koji m o ra te
d a vučete 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 statičnu unutrašnju 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 apišite 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.
Vežba20: (1) N ap rav ite interfejs koji sadrži 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.
Vežba 21: (2) N ap rav ite interfejs koji sadrži 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 spoljašnjosti iz višestruko ugnežđenih 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 ože 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
// Ugnežđene klase mogu da pristupe svim članovima
// na svim nivoima klasa u koje su ugnežđene.

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: Unutrašnje 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 išestru 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 različitih klasa. S intaksa .n e w daje o d g o v araju ću o b last važenja, p a
p ri p ozivu k o n stru k to ra ne m o ra te da n azn ačite im e klase.

Zašto unutrašnje klase?


D o sad je bilo p u n o sintakse i sem an tik e koje o p isu ju način 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 ogućnost?
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 reći d a je u n u tra šn ja klasa n alik p ro z o ru u
sp o ljašn 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, zašto n e n a p rav im spoljašnju 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, najznačajniji razlog za p o sto jan je u n u tra šn jih klasa m o že d a se fo rm u -
liše na sledeći način:
Svaka u n utrašnja klasa tnože nezavisno od drugih da nasledi realizaciju. Stoga, u n u tra -
šnja kiasa nije ograničena 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 više 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 nerešivi. led an način 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 rešen ja za
p ro b le m v išestru k o g nasleđivanja. D eo p ro b lem a rešavaju interfejsi, d o k u n u tra šn je klase
efektivno d ozvoljavaju „višestruko nasleđivanje realizacije“. U n u tra šn je klase, dakle, efek-
tiv n o o m o g u ć av a ju da nasledite više 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 načina na koje klasa mo že da realizuje više interfejsa.

interface A {}
interface B {}

class X implements A, B {}

class Y implements A {
B napraviB() {
// Anonimna unutrašnja 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 vašeg p ro g ra m a takva da o b a na-


čin a b u d u logična. 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 tačke gledišta 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 vaša 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 unutrašnje klase su
// jedini način za dobijanje efekta "višestrukog nasleđivanja realizacij e " .
package unutrasnjeklase;

class D {}
abstract class E {}

class Z extends 0 {
E napraviE() { return new E() {}; }
}
Poglavlje 10: Unutrašnje 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 „višestrukog n asleđivanja realizacije“, sve ostalo ste
sasvim lo g ičn 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 sledeće d o d a tn e m ogućno sti:
1. U je d n o m o b jek tu spoljne klase m ožete d a im ate više 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 više u n u tra šn jih klasa, o d ko jih svaka rea-
lizuje isti interfejs ili nasleđuje istu klasu na različit način . 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-
ljašnje 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 kažete „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 kreće u n a zad kroz sekvencu. O va v rsta fleksibilnosti je m o -
guća sa m o p ri u p o tre b i u n u tra šn jih klasa.
Vežba 22: (2) U d a to teci Sekvenca.java realizujte m e to d u obrnutiSelektor( ).
V ežba 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 raća 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 ešta 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 treću m e to d u
koja se kreće 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.

Zaključci 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 važenja 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 zaključak, p o što o n a sadrži delove in fo rm a c ija iz o b jek ta spoljne
klase („oblast važenja 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 ljučivanja neke vrste m e h a n izm a pokaziv ača 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 ešto 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 okazivača, 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 ašati i d a neće zlo u p o tre b iti pokazivač. K ao što ste d o sada vi-
deli, u Javi se teži ka još većoj o p rezn o sti, p a pokazivači n isu u ld ju čen i u jezik.
Z aključak koji o bezb eđ u je u n u tra šn ja klasa sav ršeno je rešenje 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 unutrašnjih 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 vaša klasa mora da realizuje metodu p o v e c a j ()


// na neki drugi način, morate da upotrebite unutrašnju 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, inače ćete dobiti
// beskonačnu rekurziju:
Pozv an a2 .t hi s. po ve caj();
}
}
Poglavlje 10: Unutrašnje 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 ođe 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 rešenje. Klasa Pozvana2 nasleđuje 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 ože-
m o da redefinišem o tako da m ože đ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 gućili bilo kakvu vezu sa sp o ljašn 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 trašn 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 ože, narav n o , sam o d a pozove m e to d u p ovecaj( ) i to je sve


(za razliku o d pokazivača 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 , nešto ka-
snije, u p o treb ljav a tu referencu za p o v ra tn i p oziv u klasu Pozvana.
Z načaj p o v ra tn ih poziva leži u njihovoj fleksibilnosti, o đ n o s n o u m o g u ć n o sti d a p ri-
likom izvršavanja d in am ičk i o d lu čite koje ćete funkcije pozvati. V red n o st ove teh n ik e p o -
staće jasnija u poglavlju Grafička korisnička okruženja, u k o m e se za p rav ljenje grafičkog
k o risn ičk o g o k ru ž en ja (G U I) svuda ko riste p o v ra tn i pozivi.

Unutrašnje 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 reše 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 više klasa i redefinišete neke m eto d e. K od koji n ap išete u red efin isan im m e to d a m a p ri-
lagođava o p šte rešenje koje p ru ž a k o stu r aplikacije d a bi rešio specifičan 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 sadrži o sn o v n u stru k -
tu r u a lg o ritm a; d a b i dovršila akciju to g alg o ritm a, o n a poziva je d n u ili više 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 slučaju, 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 ogađaje; 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 događaji (engl. event-driven system ). Jeđan o d n ajvažnijih p ro b le m a
p rilik o m pravljen ja p ro g ra m a je grafičko k o risn ičko o k ru žen je (G U I), kojim skoro p o t-
p u n o u prav ljaju događaji. Kao što ćete v ideti u poglavlju Grafička korisnička okruženja,
Javina bib lio tek a Swing je k o stu r u p rav ljanja koji efikasno rešava p ro b lem grafičkog ko-
risn ičk 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 način 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 izvrši
dog ađaje svaki p u t kada su ,,sp rem n i“. P rem d a te rm in ,,sprem an “ m ože da o značava bilo
šta, u n ašem slučaju on će biti zasnovan na časovniku. N ared n i k o stu r up ravljan ja ne sa-
drži 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 nasleđi-
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 upravljački događaj. 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 ponašanje
da upravlja oslanjajući 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


// Zajedničke metode za bilo koji upravljački događaj.
package unutrasnjeklase.upravljac;
Poglavlje 10: Unutrašnje 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() { // Omogućava 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 kašnjenja 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 ožete
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 događaj 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 došlo v rem e da se p o k re n e akcija( ). M eto d a spre-
man( ), n arav n o , m ože da b u d e redefinisana u izvedenoj klasi, kako bi izvršavanje d o-
gađaja bilo zasnov ano na nečem d ru g o m .
N aredna d ato tek a sadrži 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 više 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
// Generički kostur za sve upravljačke 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, tražeći 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 Događjaj uklanja iziiste.
U viđate d a za sad a ne zn ate n išta o to m e šta tačno 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 različite akcije različitih v rsta objeka-
ta klase Dogadjaj koje definišete praveći različite 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 ožete 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 trašn je klase se
up o treb ljav aju d a iskažu više različitih akcija koje su p o tre b n e za rešavanje 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
bašte.4 Svaka akcija je p o tp u n o različita: paljen je svetla, uklju čiv an je vode i term o stata,
uključivanje 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 različiti 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 više izvedenih verzija o sn o v n e klase D o g a d ja j. Za svaki tip akcije napravićete
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 ičajen 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 upravljački sistem koji se ceo nalazi
// u jednoj klasi. Unutrašnje klase omogućavaju da kapsulirate razlićitu
// 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: Unutrašnje klase 293

// Ovde treba da bude kod za upravljanje hardverom


// kojim se fizički pali svetlo.
svetlo = true;
}
public String t o S t r i n g O { return "Svetlo je uključeno"; }
}
public class IskljuciSvetlo extends Dogadjaj {
public IskljuciSvetlo(long vremeKasnjenja) { super(vremeKasnjenja); }
public void akcija() {
// Ovde treba da bude kod za upravljanje hardverom.
// kojim se fizički gasi svetlo.
svetlo = false;
}
public String t o S t r i n g O { return "Svetlo je isključeno"; }
}
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 puštena"; }
}
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 "Noćni režim 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 režim term o stata";
}
}
// Primer metode a k c ija ( ) koja ubacuje
// novu a k c iju u li s t u događaja.
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 događaj
dodajD ogadjaj(d);
}
s t a r t ( ) ; // Ponovo pokreni ovaj događaj
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 , većina m eto d a ak cija( ) bi uključivala n eku vrstu
up rav ljan ja h ard v ero m .
Poglavlje 10: Unutrašnje klase 295

Većina klasa izvedenih iz klase Dogadjaj slične su, ali se klase Zvono i Restart izdvajaju.
Klasa Zvono pokreće zvonce i u listu događaja dodaje još jed an objekat klase Zvono, tako
da će p o n o v o zazvoniti nešto kasnije. O b ra tite p a žn ju na to da u n u trašn ja klasa izgleda go-
tovo kao v išestruko nasleđivanje: klasa Zvono im a sve m eto d e kao i klasa Dogadjaj, a ta-
kođe 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 upravljaču. Pošto je Restart
sam o jo š je d n a v rsta klase Dogadjaj, u m eto d i Restart.akcija( ) u listu takođe m ožete 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 riše 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 riši i pokreni sistem staklene bašte.
// {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!
Noćni režim termostata
S v e tlo je uključeno
S v e tlo je is k lju če n o
Voda u stak len ik u j e uključena
Voda u stakleniku j e is k lju če n a
Dnevni režim termostata
R estarto van je sistema
Završavanje
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 ogađaj Restart se p o k reće više p u ta, a o n svaki p u t učitava 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 ože p o stići i n a fleksibilniji n ačin - u m e sto da u p išem o do gađaje 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 Grafička korisnička okruženja videćete 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 grafičkog o k ru žen ja.
K ada p ro čitate i to poglavlje, b ićete uvereni u n jih o v u v red n o st.
Vežba 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 isključuju v en tilato re. K onfigurišite 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.
Vežba 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
isključuju g e n erato re v o d e n ih kapljica. N apišite 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.

Nasleđivanje unutrašnjih klasa


Pošto 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 više ne postoji
p o d raz u m e v a n i objek at za koji bi o n a bila povezana. Rešenje je u p o tre b iti sin tak su koja
izričito o d re d u je povezivanje:

//: un u trasn jek lase/N asled iU n u trasn ju.java


// N asleđ ivanje unutrašnje 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 može 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: Unutrašnje klase 297

V idite da klasa NasleđiUnutrasnju 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 .


Vežba 26: (2) N ap rav ite klasu koja sad rži 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 sadrži u n u tra šn ju
klasu n asled en u iz prve u n u tra šn je klase.

Da li unutrašnja klasa može da se redefiniše?


Šta se dešava 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 ništa ne postiže:

//: u n u tra sn jek lase/V eli k o Ja je .ja v a


// Unutrašnja k lasa ne može 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 oče-
kivati d a će b iti po zv an a ,,redefinisana“ verzija klase Zumance, ali to se n e dešava, 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 spoljašnjiJi klasa n išta p o seb -
no ne dešava 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 izričito n asleđivanje 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 unutrašnje 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 naviše ka referenci z u klasi Jaje2. Kada
m eto d a g ( ) pozove m e to d u z .f( ) biće 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: Unutrašnje klase 299

Lokalne unutrašnje klase


Kao što je rečeno, u n u tra šn je klase se prave i u n u ta r blokova koda, najčešće u n u ta r tela
m eto d e. L okalna u n u tra šn ja klasa ne m ože im ati specifik ato r p ristu p a , p o što o n a nije deo
sp oljne klase, ali m ože 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 može 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 unutrašnjom 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 unutrašnja klasa ne može 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 unutrašnja "),
c2 = 1 ic.uzmiBrojac("Anonimna unutrašnja ");
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 unutrašnja 0
Lokalna unutrašnja 1
Lokalna unutrašnja 2
Lokalna unutrašnja 3
Lokalna unutrašnja 4
Anon im na unutrašnja 5
Anonimna uriutrašnja 6
An on im na unutrašnja 7
Anon im na unutrašnja 8
Anonimna unutrašnja 9
* ///:-

B ro ja c vraća sledeću 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 ašaju i im aju iste m o g u ćn o sti. Pošto
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 ože
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 više o b jek ata te klase.

Identifikatori unutrašnjih klasa


Pošto 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 biće n ap rav ljen e sledeće d a to te k e .class:

Bro jac.class
LokalnaUnutrasnjaKlasa$l. c l ass
Lokal naUnutrasnjaKlasa$lLokalniBrojac.class
LokalnaUnutrasnjaKlasa.klasa
Poglavlje 1 Unutrašnje 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 ačin 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 većinu situ aciia.5 Pošto 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 više na-
čin a m e n ja vaše u n u tra šn je klase kako bi m ogle d a rade.)

Sažetak
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 ništa nalik njim a. ( i zajed no rešavaju isti p ro -
b lem koji je i C + + p o k u šao d a reši p o m o ć u v išestru ko g nasieđivanja. Isp ostavilo se d a se
u C + + -u v išestru k o nasleđivanje p riličn o teško upotreb ljava, d o k su Javini in terfejsi i
u n u tra š n je Iđase m n o g o p ristu p ačn iji.
Iako su te m o g u ć n o sti p riličn 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 više ćete ih prih v atati.
Rešenja odabranih vežbi data su u elektronskom dokum entu Thin 1ln Java A n n o ta ted S olu-
tions G uide, koji se može 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 isleći 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 ičn 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 izvršavanja. Sve d o k p ro g ram
ne po čne da se izvršava, nećete znati koliko vam o b jekata treb a, niti kog su o n i tipa. D a biste
rešili 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 oći da p ri-
stu p ate preko neke reference kojoj je dodeljen o ime:

MojaKlasa referenca

po što n ik a d a nećete zn ati koliko će vam o b jek ata zaista treb ati.
Većina jezika im a neki n ač in za rešavanje ovog tem eljn o g p ro b le m a. Java n u d i nekoli-
ko n ačin 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 reči. N iz je najefikasniji n ačin č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 eličinu, a u o p šti-
je m slučaju, 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 ačin sklad išten ja objek ata; zato je n ep ro -
m enljiva veličina niza pretešk o og ran ičen je.
Za rešavanje 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 ačav an je o d re d e n o g p o d sk u p a te biblioteke, ko-
ristiću 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 način e za
sm eštan je o b jek ata i p o m o ć u njih m o žete d a rešite izn e n a đ u ju ć e veliki bro j p ro b lem a.
P ored o stalih svojih obeležja —na p rim er, Set (sk u p ) ne m o že da sađrži p o n o v ljen e ele-
m en te, a M ap (m ap a) je asocijativan niz koji služi 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 podešavaju svoju veličinu. 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 pišete 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 ršk u kroz rezervisane reči,' k o n tejn ersk e klase su jed-
na o d n ajsn ažnijih alatki za p ro g ra m ira n je , je r p rim e tn o sk raću ju v re m e pisan ja p ro g ra-
m a. U ovom poglavlju steći ć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 avićem 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 korišćenja.

U građenu p o dršk u za kontejnere im aju b ro jn i jezici, m eđu kojim a Perl, P vthnn i Ruby.
Poglavlje 11: Čuvanje objekata 303

Generičke 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 rišćen ja k o n tejn era b io je to što je prevodilac
d o p u šta o u m e tan je pogrešn 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 poćetak, sm atrajte da je ArrayList „din am ičk i niz koji se au to m atsk i p ro širu je“.
K orišćenje 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 korišćenje indeksa, baš
kao u slučaju niza, ali bez uglastih zag rad a.2 ArrayList im a i m e to d u siz e ( ) k oja vraća tre-
n u tn i bro j elem en ata i tim e vas sprečava 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 izvršavanja“ (engl. ru n tim e exception); izuzeci će b iti p redstavljeni u poglavlju
O brada grešaka pom oću 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. Pošto u p rim e ru nisu u p o tre b lje n i g en eričk i tip o v i, Javin p rev o d ilac će vas
u p o zo riti n a grešku. D a b ism o sprečili 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 Narandže:
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 vršavan ja
}
} /* (Po k re n ite da b is te v id e li r e z u lta t) * / / / ■ -

Više o a n o ta c ijam a u Javi SE5 saznaćete u poglavlju Atiotacije.


Klase Jabuka i Narandza se razliku ju ; n e m a ju n išta zajedničko, sem što p o tič u o d n a t-
klase Object. (N e zaboravite: uk olik o izričito ne n avedete klasu o d koje nasleđujete, au -
to m atsk i n asleđ ujete Object.) P ošto ArrayList sadrži 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 izvršno
o k ru žen je Jave u v rem e izvršavanja. 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 ašem m išljen ju o b jek ti tip a Jabuka, v ratiće 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-
zvaćete sin ta k sn u grešku.
U v re m e izvršavanja, k ad a o b jek at tip a Narandza p o k u šate da svedete na tip Jabuka,
javiće se greška 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 enerički tipovi sazn aćete d a pravljenje klasa p o m o ć u odg ov arajućeg Javi-
n o g m e h a n iz m a u m e d a b u d e složeno, ali d a je p rim e n a je d n o m d efin isanih generičkih
ldasa najčešće 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 išete 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 više) 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 eneričk ih tip o v a vas već u vrem eprevođenja sprečava da u k o n te jn e r sta-
vite pog rešan 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 generičkih
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 );
// Greška koja se p r i j a v l j u j e u vreme prevođenja:
// 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 Cetterički 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 više 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 sprečiti 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
greška p rijav iti u v rem e p rev o đ en ja, a ne u v rem e izvršavanja.
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 svođenje
tip o v a. P ošto Lista zna koji tip sadrži, o n a obavlja svođenje k a<ta vi pozovete g et(). D akle,
ne sa m o da p rev o d ilac zbog gen eričk 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 sledeće: 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 ožete 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 enerički 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 svođenje naviše rad i i s gen eričk 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 sadrži 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 eriše m eto d a hashCode()). Heš kodove ćem o
detaljno razm o triti u p o glavlju D etaljno razm atranje kontejnera.
Vežba 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 skače. 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, izražena 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 više pravila. List (lista) m o ra d a čuva elem ente u o d re đ e n o m redosledu, Set
(skup) ne m ože da sadrži p o n o v ljen e elem ente, a Queue (red) daje elem ente red o m
koji zadaje disciplina čekanja (o b ičn 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 alažen je objekta
p om oću njegovog ključa. ArrayList o m o g u ćav a p ro n alažen je objekta p o m o ć u nje-
govog indeksa, pa n a neki n ačin p rid ru ž u je brojeve o b jek tim a. M apa om ogućava
pronalaženje 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 rečnik,
zato što objekat traži p o m o ć u ključa, k ao što se u rečniku definicija traži p o m o ć u
reči. Mape su m o ć n e p ro g ra m sk e alatke.
Iako to nije uvek m oguće, bilo bi id ealn o da veći d eo vašeg k oda rad i s tim interfejsim a,
a da jedino m esto na kojem ćete sp o m e n u ti tačan 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 računa o to m e da je ArrayList sveden naviše 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, obično pravite o b jek at k o n k re tn e klase, svedete ga naviše na odgovarajući in-


terfejs i zatim u ostatku k o d a u p o treb lja v ate taj interfejs.
Ovaj pristu p neće 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 sadrži, a TreeMap im a m e-
tode koje interfejs Map ne sadrži. A ko v am tre b a ju te m eto d e, nećete m oći d a svedete na-
više na opštiji interfejs.
Interfejs Collection p o o p šta v a ideju sekvence - način 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 ošto 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 sadrži 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 slučaju klase ArrayList i
svih vrsta Listi, add() uvek znači ,,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 čićete da u p o treb ljav ate fleksibilniji Iterator.
Vežba 2: (1 ) Izm enite p ro g ra m JednostavnaKolekcija.java tako da se za c upotrebljava Set.
Vežba 3: (2) Izm en ite p ro g ra m unutrasnjeklase/Sekvenca.java tako da m u m ožete 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 (koristeći 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 sadrže 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 brže, a l i ovako ne možete
// 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 "podržanu" 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); // Greška pri iz vršav an ju zato što se
// pripadnom nizu ne može m enjati v e lič in a .
}
} III--
K o n stru k to r za Collection m ože 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 dužine.
Izlaz m etod e Arrays.asList() m ožete u p o tre b iti i n ep o sred n o , kao Listu, ali je u to m
slučaju on predstavljen nizom , a njegovu veličinu ne m o žete m en jati. U koliko takvoj listi
pok ušate da d o d ate - add() - ili o d u z m e te - delete() - elem en te, tim e biste pokušali da
pro m en ite veličinu niza, što u v rem e izvršavanja izaziva grešku „ U n su p p o rte d O p e ra tio n “.
O g ran ičen je m eto d e Arrays.asList() jeste to što o n a sa m o nagađa koji je rezu ltu ju ći tip
Liste i ne obraća pažnju 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 nagađa 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 ( ) ) ;

// Neće 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 () neće 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 );

// Daćemo 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 okušava 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 idećete), 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 gačije sem p rek o sadržaja neke d ru g e M ape.

Ispisivanje sadržaja kontejnera


Sadržaj 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 najvažnije 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


// Sadržaj 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("mačka“ , "M ica'1) ;
m apa.put("kuče", " L e s i " ) ;
310 Misliti na Javi

m apa.put(''kuče", “ 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, mačka, kuče, kuče]
[pacov, mačka, kuče, kuče]
[kuče, mačka, pacov]
[pacov, mačka, kuče]
{kuče=Bobi, mačka= M ica, pacov=Zbunjeni}
{mačka= Mica, kuče=Bobi, pacov=Zbunjeni}
{pacov=Zbunjeni, mačka= M ica, kuče=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 ačin p o sm a tra n ja sekvence, pa zato nije p rik a z an ). Mapa na
svakoj lokaciji sadrži 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 ponašanje što se tiče ispisivanja (izlaz
m eto d e toString() svakog k o n tejn era) daje priličn 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 vitičastih zagrada, p ri čem u su ključevi i n jim a p rid ru ž e n e
v red n o sti povezane zn ak o m jednakosti (ključevi 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 eđu 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 sadrži više operacija o d tip a
ArrayList. O n jim a ćem o više 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 ože sad ržati sam o po jed an p rim e ra k id e n tič n ih elem enata,
ali i da različite realizacije sk u p o v a e lem en te skladište na različite način e. T ip HashSet
skladišti elem en te na p riličn o k o m p lik o v an način 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 rže vađenje elem enata, iako red o sled njegovog skladištenja izgleda b esm islen (često je
važno 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 značaja). A ko je p o re d a k skladištenja važan, 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 alažen je o b jek ta p o m o ć u nje-
govog ključa, 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 država 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 retraživ an je ob avljate p o -
m o ću ključa ,,G ruzija“ - gotovo kao da je u p ita n ju indeks niza. Z bog takvog p o n ašan ja
M ap a p rih v ata sam o po jed an p rim e ra k svakog ključa.
Map.put(ključ, vrednost) u m eće u m a p u v re d n o st (o n o što hoćete d a u sk lad ištite) 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 aći).
Map.get(ključ) daje v red n o st p riđ ru ž e n u d a to m ključu. 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 retraživ 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 veličinu M ap e (n iti se za to u o p šte starajte), je r o n a
svoju veličinu podešava a u to m atsk i. P o red toga, M ap e sam e u m e ju d a isp išu svoj sadržaj,
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 ključevi 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 ržu te h n ik u p retraživ an ja; takođe,
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
ključevi 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 ržan a b rz in a p retraž iv a n ja tip a HashMap.
Vežba 4: (3) N apravite generatorsku klasu koja vraća im ena (kao objekte tipa String) likova
vašeg o m iljeno g film a (ili Snežane i sedam p atuljaka ili Ratova zvezda, nije važno) svaki p u t
kada pozovete next(), i vraća 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 ičn o g niza i po jed n o g p rim e rk a tipova
ArrayList, LinkedList, HashSet, LinkedHashSet i TreeSet. Z atim ispišite sadržaj 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
više 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 košta 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 ičn im red o sled om , ali su njegove m o g u ć n o sti
veće nego o ne tip a ArrayList.
312 Misliti na Javi

U n a re d n o m p rim e ru uvešćem 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 rži 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) statičn a m eto d a
Pets.arrayList() vraća ArrayList p o p u n je n n a su m ičn 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 podešava 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 tačno određeni 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()); // Umeće 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 važan:
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 ); // Izmešaj
print("izm ešana 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.ačrio 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 ); // Umeće 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
izmešana 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 podešava svoju veličinu. To je n jen a osnov-
n a vrednost: sekvenca koja se m ože 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 akođe, 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 znači d a taj n o v i nije p ro n a đ e n ), pa će i p o-
kušaji u k lan jan ja tog objekta m e to d o m remove() v ra titi false (što zn ači 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 sadržaj o b a zn ak o v n a niza id en tičan . 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 aša 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 tačn 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 zaključak: 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 slučaju , 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 znači 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 reći na LinkedList čini takvo nešto zatreba? Ne
- o to m e p ro sto tre b a da v o d ite raču 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 vaša 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 složen 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 loše zn ati o čem u se rad i).
M eto d a subList() služi za pravljenje isečka veće liste, pa je p riro d n o da se d o b ija rezul-
tat true kada se isečak p rosledi m e to d i containsAll() za tu veću listu. Z an im ljiv o je da pri
to m p o re d ak nije važan - 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 utiče po zivanje m eto d a C ollections.sort() (za so rtira n je ) i Collec-
tions.shuffle() (za m ešan 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 vraćen o j listi od ražav 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 slučaju,
o na u o b je k tu kopija zadržava sve elem en te koji su i u p o d listi sub. I o p et, rezu ltu ju će po -
našanje 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 ašan je m eto d e removeAll() zavisi o d m e to d e equals(). Kao što joj i englesko ime
kaže, 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 loše n azv an a, je r ju je m og uće 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
služi za u m e ta n je nove liste u sred p rv o b itn e , u m esto o b ičn 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 vraća 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 ladištenje svih ob jekata u Listi (kao što je ovde
slučaj), m e to d a toArray() će n a p ra v iti n o v n iz od g o v araju će veličine. 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.
Vežba 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.
Vežba 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.
Vežba 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 ačin 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 ešto sadrži. 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 ačin a da se pro čitaju.
Ako p o č n ete da razm išljate na višem 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 loše, ali šta ako n ap išete 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 išete generički 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 pišete ponovo?
O vakva apstrak cija se p o stiže k o rišćen 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 važno) 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 iđati naizgled ču d n a o g ran ičen ja; na p rim er, Ja-
vin Iterator m ože da se kreće sa m o u je d n o m sm e ru . Sa ite ra to ro m ne m ožete 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 sledeći 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 može 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 okušavate d a m o d ifik u jete sam L ist objekat.
Ite r a to r takođe 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 znači 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 znači 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 orišćenje k o n tejn e ra i k re tan je k ro z njega d a b i se izvršavale 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 zapazićete 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 oćili ste d a m eto d a ispisiS ve() ništa 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đ kaže da ite ra to ri objedinjuju pristup kontejneritna.
V ežba 8: (1) P repravite vežbu 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 ežba 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 ežba 10: (2) P repravite vežbu 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 ežba 11: (2) N apišite 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 ispišite sadržaj 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 ože 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 sledećeg 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 ićete
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 iće 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 .
Vežba 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 veličine 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 rešite na više
načina .)
Poglavlje 11: Čuvanje objekata 319

Ulančana 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 tičn e su - o n e vraćaju čelo (p rv i elem en t) liste, a da
ga ne u k lo n e, i gen erišu izuzetak NoSuchElementException ako je Lista p razn a. M eto d a
peek() je n jihov a varijacija koja vraća 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 raćaju kao rezu ltat
čelo liste, i g enerišu izuzetak NoSuchElementException ako je lista p raz n a, d o k n jih ov a
varijacija poll() 11 to m slučaju vraća null.
addFirst() um eće 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 vraća poslednji elem en t liste.
N ared n i p rim e r p ok azu je o snovne sličnosti 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 ašan 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 tičn 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 vraćaju 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 videćete 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.
Vežba 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 događaja p rim e n ite Iterator.
Vežba 14: (3) N apravite p ra z n u listu LinkedList<Integer>. U sred in u Liste d o d ajte Inte-
gere pomoću Listlteratora.

Pravljenje steka od ulančane liste


Stek se p o n ek ad naziva LIFO k o n te jn e r (engl. last-in, first-out, poslednji koji uđe, prvi
izlazi). To znači 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 ožete 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 ožete u p o tre b iti u la n č a n u listu. U n ekim slučajevim 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 generičke 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 efinišem 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 rečen o da čuva tip T. O b ra tite p ažn ju na to d a p u sh ( ) u zim a o b jek at
tip a T koji v raćaju p e e k ( ) i p o p ( ). M eto d a p e e k ( ) vraća najviši 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 vraća.
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, nasleđivanje 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 (videćete u p o glavlju D e-
taljno razm atranje kontejnera d a su tak v u g rešku 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 raćete 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
sprečili 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 ički interfejs
Stack - verovatno zato što je o rig in a ln a, loše 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 ožete 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.
Vežba 15: (4) U p ro g ram sk im je z iđ m a stekovi se često u p o treb ljav aju za izračun avan je
v red n o sti izraza. Izraču n ajte sledeći izraz koristeći paket net.m indview.util.Stack, gde +
znači „stavite n a stek sledeće sIovo“, a - zn ači „skinite v rh steka i ispišite 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 sadrži više 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 više od je d n e instance ekviv alen tno g objekta, Set će sprečiti d u p liran je . Set se
najčešće 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 pretraživ an je o b ič n o najvažnija o p eracija, pa se
najčešće b ira realizacija HashSet, o p tim iz o v an a za b rz o pretraživ anje.
Set (sku p ) im a isti interfejs kao Collection, što znači da nem a d o d a tn ih fu nk cija kao
što im aju dve različite liste. S kup je isti kao kolekcija, sam o se različito p o n aša. (O vo je
p rim e r savršenog korišćenja n asledivanja i p o lim o rfizm a: za izražavanje d rug ačijeg po-
n ašanja.) 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 jašn je n je o d čega se
sastoji v re d n o st objekta složeno je, kao što ćete v ideti u poglavlju D etaljno razm atranje
kontejnera.
Poglavlje I 1: Čuvanje objekata 323

Sledeći p rim e r pokazuje po n ašan 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 sadrži
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 ključa (engl. hashing), koje HashSet u p o treb ljav a rad i veće b rzin e - o n o je o b -
jašn jen o u poglavlju D elaljno razm atranje kontejnera. R edosled koji o država HashSet
razlikuje se od redosleda u TreeSet ili LinkedHashSet, p o što svaka realizacija skladišti
elem en te n a različit način. 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 ključa, p ro je k to v an o specijalno rad i b rzo g pro-
nalaženja. 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 ključa 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 ržava 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 ajče šć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 učili 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


naći 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 osluži. N a p rim er, recim o kako h oćete da
ispišete sve reči d ato tek e OperacijeSaSkupovima.java. Za o tv ara n je i učitavanje 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 ićem 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 reči u sk lad u s regularnim izrazom \W + , koji zn ači „jed n o ili više slova“ (reg u la rn i iz-
razi su o b jašn 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 sadržaj Liste d o d a je sebi. P ošto je u p ita n ju TreeSet, rezu ltat je u re d en . U
o v o m slučaju, 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.


Vežba 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 ispiše b ro j sam o g lasn ik a u svakoj ulaznoj reči, 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 ačin rešavanja 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 slučajnosti brojeva iz
Javine klase R a n d o m . U id ea ln o m slučaju, R a n d o m bi davala savršeno raspodeljene
slučajne brojeve, ali da biste to ispitali, m o ra te d a generišete više slu čajn ih brojeva i da iz-
b ro jite o n e koji p rip a d a ju različitim opsezim a. M a p a rešava taj zad atak s lakoćom ; u
ovom slučaju, 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 između 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 ože staviti u m a p u tipa H ash M a p . (U k o n tejn e r ne m ožete
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() vraća n u ll
ukoliko takvog ključa nije b ilo u k o n te jn e ru (što znači 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 vraća p rid ru ž e n u In te g e r v re d n o s t ključa koja se povećava 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 sađrži 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 mačka", 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 rčak ", 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 mačka=Cat Mica, Moj hrčak=Hamster Boško, Moj pas=Dog Bruno}
Dog Bruno
true
true
* ///:-

M ap e jc lako p ro š iriti n a više 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 više 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 ože da vrati skup svojih ključeva, 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 ključeva 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.
Vežba 17: (2) K lasu MorskoPrase iz vežbe 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 alažen je
o b jek ta klase MorskoPrase za svaki ključ, p o to m ispišite svaki ključ i p o zo v ite m e to d u
sk a ce( ) za svaki objekat.
Vežba 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. Ispišite rezu ltate tako
da po k ažete u re đ e n je p o heš k o d u . Izvadite p arove i u red ite ih p o k ljuču, a rez u ltat stavite
u m a p u LinkedHashMap. Pokažite d a je red o sled u m e ta n ja zad ržan .
Vežba 19: (2) P o n o v ite p re th o d n u vežbu sa sk u p o v im a HashSet i LinkedHashSet.
Vežba 20: (3) P rep rav ite vežbu 16 tako d a se b ro ji učestalo st pojave svakog sam oglasnika.
Poglavije 11: Čuvanje objekata 329

Vežba 21: (3) K oristeći Map<String,Integer> i o b lik p ro g ra m a JedinstveneReci.java,


napišite p ro g ra m koji b ro ji učestalo st pojave reči 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 ažite rezu ltat.
Vežba 22: (5) P reprav ite p re th o d n u vežbu ta k o d a u p o treb ljav a klasu koja sadrži String i
po lje b ro jača za skladištenje svih različitih reči, i Set tih o b je k a ta za o d ržav an je liste reči.
Vežba 23: (4) N a o sn o v u p ro g ra m a StatistickiPodaci.java na p išite p ro g ra m koji staln o
izvršava test i prov erav a da li se u rezu ltatim a n ek i b ro j p ojavljuje češće o d d ru g ih .
Vežba 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 ljuču i p o n o v o u m e tn ite u Mapu.
Vežba 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 reči to k o m u čitav a n ja i za svaku reč u
d ato teci, zapišite 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 .
Vežba 26: (4) P reprav ite rezu ltu ju ću Mapu iz p re th o d n e vežbe tak o d a p o n o v o u sp o -
stavite p o re d a k reči iz o rig in aln e datoteke.

Pravljenje reda za čekanje (od ulančane 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 znači 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 ičn 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
izvršavanje, redovi za čekanje su n aro čito važni 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 ašaju red za ček an je i realizuje interfejs Queue, pa se
m ože u p o tre b iti za realizovanje reda za čekanje. Pošto je L in k e d L ista svedena naviše na
Q u e u e , u ovom p rim e ru u p o treb ljen e su m e to d e specifične 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 svođenjem naviše
// 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 specifičnih za k lasu Queue; o n a u m eće elem en t na


re p red a za čekanje ako m o že, ili vraća false. M eto d e p e e k ( ) i elem en t( ) v raćaju čelo red a
za čekanje, a da ga ne uklone, ali p e e k ( ) v raća null ako je red za čekanje p ra za n , d o k
elem en t( ) g eneriše 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 raća null ako je red za čekanje p ra za n ,
d o k rem ove( ) u to m slučaju generiše 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 sužava 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 težan o ).
Im ajte u v id u da m e to d e specifične 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 rečim 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 asleđen.
Vežba 27: (2) N apišite klasu Naredba koja sadrži je d a n String i im a m e to d u operacija( )
koja ga p rik azuje. N apišite 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 vraća taj red za čekanje. P o p u n jen Queue p ro sled ite m eto d i u trećoj 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 ušao “ (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 sledeći izlazi iz reda. Po p rin c ip u FIFO, sledeći 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 najveću p o tre b u
(p rio rite t) d a izađe. N a p rim e r, na a e ro d ro m u se iz red a za čekanje izvlači 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 važnije 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 ašan 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 ačen n a odgovarajuće 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 ožete 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 ajvišim 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 slučajn 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 rža 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 najviši 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 viši 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 , treći 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 iše 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 aći ćete i ta n a n iji p rim e r koji to p o k azu je .
Vežba 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 ažite ih.
V ežba 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-
kažite d a ne m ožete usp ešn o d o d a ti više 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 jašn je n o u poglavlju D etaljno razm atranje kontejnera.

Poređenje kolekcija i Iteratora


Collection je ko renski interfejs koji o p isu je o n o što je zajed n ičk o svini k o n tejn erim a se-
kvenci. M ožete ga sm a tra ti „slu čajn im in terfe jso m “ koji je n astao zbog m e đ u so b n e
sličnosti 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 ožete 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 opštijeg
koda. A ko je k o d nap isan za interfejs a ne za realizaciju, p rim en ljiv je n a više tipova obje-
kata.6 U koliko n ap išem m e to d u koja u zim a Collection, ta m eto d a se m ože 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 više 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 zajedničko, postiže se ite ra to rim a . M ožda b i u Javi bilo p a m e tn o slediti p rim e r iz
C + + -a i izraziti sličnost 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 znači 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 slučaju, ob a p ristu p a su jed n ak a. U stvari, Collection je n ešto bolji p o što se
m ože 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 ešto č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 teško 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 sadrži 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 sledeće: 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 nasleđi-
van ja klase AbstractCoIlection. M e đ u tim , u k o lik o vaša klasa već nasleđuje d ru g u klasu,
n e m o že naslediti i AbstractCollection. U to m slučaju, 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 lakše 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 način povezivanja sekvence i m eto d e koja p rim a
tu sekvencu, a pri to m se klasa sekvence o g ran ičav a z n a tn o m anje nego kada se im ple-
m e n tira klasa Collection.
Vežba 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 ošto 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 iše zato što je Java SE5 uvela nov interfejs nazvan Iterable koji sadrži 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 funkcioniše 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( ) vraća 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 više 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( )' vraća 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 ože 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 znači 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 nešto 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, pošto se smatralo da bi bila previše usko povezana sa opera-
tivnim sistemom, pa bi se kršilo pravilo„napiši jednom, izvršavaj bilo gde“. Činjenica da metoda sada
postoji k.izuje da su projektanti Jave nakon pojave .NET-a postali pragmaličniji.
338 Misliti na Javi

}
} /* Is p is :
1 2 3 A B C
* ///:-

Pokušaj 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 .
Vežba 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 kažite 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 više n o v ih n ačin 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 reči ite rira ti u n a p re d ili u n azad . U koliko sam o n asledite tu klasu i redefinišete
m e to d u iterator( ), zam en ićete p o sto je ću m e to d u i nećete m o ći d a b irate.
Jed n o rešenje sam n azvao A dapterska m etoda. „A dapterski“ d eo p o tiče 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 rešiti u koliko napišete 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 redefinišem . 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 ože u p o tre b iti u foreach naredb i. Kao što ćete videti, tim e
d o b ija m o više n ačin a u p o tre b e fo reach sintakse:

//: cuvanje/AdapterskaMetoda.ja va
// "Adapterska metoda" omogućava 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 običan 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 ićete (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 ačije
p o n ašan je.
Na taj način , 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


vraća o n aj iz izm ešane 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 eša 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 ešan 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 mešanja: " + 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 mešanja: " + 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 mešanja: " + 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 mešanja: " + 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 mešanja: [1 , 2, 3, 4, 5, 6, 7, 8, 9, 10]
Nakon mešanja: [4, 6, 3, 1, 8, 7, 2, b, 10, 9]
n iz : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Pre mešanja: [1 , 2, 3, 4, 5, 6, '/, 8, 9, 10]
Nakon mešanja: [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 slučaju, 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 ešan 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?iči< 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 .
Vežba 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.

Sažetak
Java im a više 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 ršite k rziju tip o v a. N iz m ože da
b u d e v iše d im e n z io n ala n i da sadrži p ro ste tipove. M e li m , ne m o žete d a m u p ro -
m en ite veličinu.
2. K olekcija sadrži p o jed in ač n e elem en te, d o k m a p a sad rži povezane parove. P o m o ču
Javinih g en eričk 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 rešan 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 veličinu n akon
d o d a v a n ja i u ld an jan ja elem en ata. K o n tejn er r»e uiože ad rži 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 tačk 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 eričk 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 ečete 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 način 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 ključeve 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 različitih v red n o sti. Klasa HashSet o b e z b e đ u je p ro n alažen 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 ajčešće ćete u p o tre b lja v ati k o n te jn e re oivičene
d eb e lo m p u n o m lin ijo m .
T ačkastim o k v irim a obeleženi su interfejsi, a o k v irim a s p u n o m lin ijo m o b ičn 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 ože da prav i o b jek te klase na koju po k azu je
strelica. Na p rim er, bilo koja kolekcija m ože da pravi Iterator, d o k List m o že da n ap rav i
Listlterator (kao i ob ičan 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 enerički 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 tačk a klasa Map i C ollection jeste činjenica da
Map m ože 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 pažnju na interfejs java.util.RandomAccess koji je prikačen uz ArrayList, ali


ne i uz LinkedList. Time se pruža informacija algoritmima čije če se ponašanje dinamički
menjati u zavisnosti od upotrebe određene Liste.
Ova organizacija je malo čudnija od objektno orijentisane hijerarhije. Međutim, kako
budete više saznavali o kontejnerima paketa java.util (naroćito u poglavlju Dctaljno ra-
zmatranje kontejnera), videćete da nije problematična samo čudna struktura nasleđivanja.
Oduvek je bilo teško projektovati biblioteke kontejnera, jer treba zadovoljiti međusobno
suprotstavljene sile. Zato poneki kompromis ne bi trebalo da vas iznenadi.
Uza sve to, Javini kontejneri su osnovne alatke kojima svoje programe možete učiniti
jednostavnijim, snažnijim i delotvornijim. Možda će vam trebati malo vremena da se na-
viknete na određene aspekte ove biblioteke, ali mislim da ćete njene ldase brzo početi da
nabavljate i upotrebljavate.
Rešenja odabranih vežbi su u elektronskom dokum entu The Thinking in Java Annotated Solution
Guide, koji se može kupiti n a lokaciji www.MindView.net.
Obrada grešaka pomoću 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 greške 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 greške o tk riti p ri p re -
v o đ e n ju . N eki p ro b lem i se m o ra ju rešavati 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 greške 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 teškoćom .
P o b o ljšan o op o ravljan je o d grešaka je d a n je o d n ajb o ljih n ačin a za po većanje ro b u -
sn o sti k o d a. O poravljan je o d grešaka je najvažnije za svaki p ro g ra m koji pišete, 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 grešaka p o m o ć u izuzetaka.
C iljevi o b ra d e grešaka 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 oguće, i u z povećavan je v ero v atn o će
da se u aplikaciji neće p o jav iti greška koja nije o b rađ e n a. Izuzetke nije tolik o teško 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 ošto je o b ra d a izuzetaka je d in i zvan ični način n a koji Java prijavlju je greške,
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 erišete sopstvene izuzetke k ad a se neka o d vaših 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 grešaka 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
vraćala 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 epogrešivim i razm išljaju u sti-
lu: ,,Da, greške se m ožd a dešavaju 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 greška (a p o n e k a d su uzroci
greške bili previše glupi d a bi se p ro v erav ali).1 Kada biste bili d o v oljn o pažljivi d a tražite
grešku posle svake m eto d e, vaš k o d bi se p retv o rio u nečitljivu n o ć n u m o ru . P ošto su p ro -
gram e.ri i dalje m ogli da o d ržav 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 grešaka u m n o g o m e je og ran ičav ao pisanje
velikih, sn a ž n ih p ro g ra m a koji bi se lako održavali.
R ešenje je d a se p o stu p k u o b ra d e grešaka o d u z m e p riro d n o st i da se ojača fo rm aln o st.
O vaj način , zapravo, im a d u g u isto riju , je r se obrada izuzetaka (engl. exception handling)
sreće 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 grešaka u jeziku C + + zasnivala
na jeziku A da, a Javina p retež n o n a C + + - U (iako više podseća 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 ećete 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 ože 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 rešili p ro b lem . Z bog tog a ga
p ren o site n a viši 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 reši.
Još je d n a p riličn o k o risn a o so b in a izuzetaka jeste da o n i o b ičn o p o jed n o stav lju ju kod
za o b ra d u grešaka. D a n e m a izuzetaka, m o ra li b iste d a tra ž ite o d re đ e n u g rešku 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, više n e m o rate da p rov erav ate
greške 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
rešavate na je d n o m m estu , a to je tzv. blok za obradu izuzetaka (engl. exception handler).
O n skraću 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
izvršavanja, o d k o d a koji se izvršava k a d a n ešto k ren e n aop ak o. O b ičn o je k o d za u p isi-
vanje, čitanje i p ro n a la že n je grešaka 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 greškam a.

Osnovni izuzeci
Vanredno stanje (engl. exceptional condition) jeste p ro b le m koji sprečava n astavak ra d a te-
kuće m e to d e ili p ro g ra m sk o g blo k a. V ažno je razlikovati v a n re d n o stanje o d u o b ičajen 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 tekućem okruženju. M o žete sam o d a iskočite iz tekućeg o k ru ž en ja i
da p ren esete p ro b le m u više o k ru žen je. To se dešava 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 ači k ad a je delilac nula? M ožda znate, u k o n tek stu p ro b le-
m a koji p o k ušavate da rešite 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 generišete izu zetak u m e sto da nastavite s to m p u ta n jo m izvršavanja.
K ada n astan e izuzetak, dešava se n ekoliko stvari o d je d n o m . Prvo, p rav i se objekat izu-
zetka na isti n ačin kao svi d ru g i Javini o bjekti: u d in am ičk 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 izvršavanja (o n a koja se ne m ože 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
izvršavanja 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 arajuće m esto
s kog bi se nastav ilo izvršavanje 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 reši p ro b lem p rim e n o m d rugačijeg p ri-
stu p a p ro b le m u , ili d a p ro sto nastav i izvršavanje o d neke d ru g e tačke.
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 ože 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 ožete da pošaljete in fo rm ac ije o grešci u više 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 grešaka pomoću izuzetaka 347

O v im se g eneriše 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, biće 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 izraču 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 izračunavanje."2 Izuzetke m o žete
sm a tra ti i u g ra đ e n im siste m o m za p o n ištav an je, p o što (u z m alo pažnje) u p ro g ra m u
m o žete im a ti više m esta o p o rav k a. A ko je d a n d eo p ro g ra m a zakaže, 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 ajvažn ijih asp ek ata izuzetaka jeste sledeći: u koliko se d o g o d i nešto loše, o n i
p ro g ra m u n e dozvoljavaju d a n astavi izvršavanje 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 ože p ri-
m o ra ti p ro g ra m d a p re k in e sa izvršavanjem 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 arajuće stanje. A ko n išta 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 slučaju) da n a te ra te p ro g ra m d a reši 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 ičk 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 važne 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 inože 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 dešavanja. N akon što o p e ra to ro m
new n ap ra v ite objekat koji pređstavlja izuzetak, d o b ijen u referencu ćete p roslediti rezervi-
sanoj reči th ro w . T im e se postiže da se taj objekat v rati iz m etode, iako o n a o b ičn o nije p ro -
jek to vana da vraća taj tip objekta. O b rad u izuzetaka p ojednostavljeno m ožete shvatiti kao
drugačiji m eh a n iza m vraćanja rezultata, m ad a ovu analogiju n e bi trebalo da shvatate su-
više doslovno. I o b ičn i p ro g ram sk i blokovi m o g u se p rek in u ti generisanjem izuzetka.
U svakom slučaju, vraća se objekat izuzetka, a m eto d a ili p rogram ski b lo k se n ap u štaju .
Svaka sličn o st sa o b ič n im p o v ra tk o in iz m e to d e tu se i završava, 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 ičajen o g poziva m eto d e. (O brešćete 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 ože da
b u d e u daljen više nivoa o d m esta gde je izuzetak nastao.)
Pored toga, m ožete d a generišete 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 ačiju klasu izuzetaka za svaki tip greške.

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 grešci 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 višem o k ru ž en ju shv atio šta da ra d i s vašim 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 nešto 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 eriše 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 erišete 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 generiše 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 iča 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 može da generiše izuzetke
)

Kada ste pažljivo tražili greške u p ro g ra m sk o m jezik u koji ne p o d rža 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 grešaka,
č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 grešaka koji se ko-
risti u Javi, sve što treb a ispitati sm ešta se u isp itn i blok, a svi izuzeci se h v ataju na istom
m estu . To znači da se k o d m n o g o lakše piše i čita, je r se njegova sv rh a ne p rep liće s p ro -
v erom grešaka.

Blokovi za obradu izuzetaka


G enerisani izuzetak m o ra negde da se završi. Z avršiće 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 ačen i su rezervisa-
n o m reči catch:

tr y {
// Kod k o ji može da generiše 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 grešaka pomoću 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 izvršava 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 raženje blo -
kova za o b ra d u izuzetak a zaustavlja se p o izv ršav an ju to g b loka. Izvršava 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 sprečilo izvršavanje 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 različitih m eto d a
m o ž e da generiše 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 ržav 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 grešk 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
način za izlazak iz te situacije, i da neće d a se v ra ća nazad .
A lternativa se naziva nastavljanje (engl. resum ption). To zn ači da se o d blo k a za o b ra d u
i/.uzetaka očekuje d a u rad i n ešto kako bi rešio situ aciju , n a k o n čega se p o n o v o p o k u šav a
s izvršavanjem 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 izvršavanje b iti
uspešno. Ako k o ristite ovaj m o d el, znači da se i d alje n ad a te kako ćete n astav iti izvršava-
nje n ak o n o b ra d e izuzetka.
U koliko h o će te d a n astav ite izvršavanje, n e m o jte g en erisati izu zetak k ad a n aiđ ete n a
grešku, već p o zovite m e to d u koja rešava 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 zadovoljavajući.
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 počeli da k o riste isključivo m o d e l p rek id an ja. Iako nastavljanje na prv i
po gled izgleda privlačno , nije toliko k o risn o u p raksi. N ajvažniji razlog v ero v atn o je po-
vezanost koja nastaje: nastavljački 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 ržati negeneričk i kod specifičan za m e sto p o jav ljiv an ja izuzetka. To otežava p i-
sanje i o d ržav 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 jeće 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 greške 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 ačiti p o se b n e p ro b le m e n a koje vaša bi-
blioteka m ože d a naiđe.

' 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

Vašu klasu izuzetka m o ra te d a izvedete iz neke o d po sto jećih klasa izuzetaka, ako je
m og uće iz o n e ćije je značenje blisko sm islu novog izuzetka (m a d a to često nije m o gu će).
N ajp rostiji način 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 Nasleđivanjelzuzetaka {
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) {
Nasleđivanjelzuzetaka 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 nećete 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, najvažnija o so b in a izuzetka je im e klase, p a je u najvećem b ro ju slučajeva izu-
zetak p o p u t p rik azan o g sasvim zadovoljavajući.
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 grešku hteti da pošaljete u s ta n d a rd n i iz-
lazni to k za greške, tako što ćete je upisati u to k p o d atak a System.err. To je, o b ičn o , bolje
m esto za slanje inform acija o grešci 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 neće 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 veća verovatnoća da će ih k o risn ik p rim etiti.
M ožete 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 grešaka pomoću 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 sadrži sam o dva k o n stru k to ra koji d efin išu n ačin 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 reči 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 izvršavanja š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 greške.


Vežba 1: (2) N apravite klasu s m e to d o m main( ) koja generiše 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

Vežba 2: (1) D efinišite referen cu n a o b je k at i in icijalizujte je vre d n o šć u null. P okušajte 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.
Vežba 3: (1) N apišite 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).
Vežba 4: (2) N ap rav ite so p stv en u k lasu izuzetaka k o rišćen jem rezervisane reči extends.
N apišite 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 ešta ga u objekat
s referencom tip a String. N ap išite m e to d u koja šta m p a saču vani znakovni niz. N apravite
b lo k try-catch da biste isp ro b ali n o v izuzetak.
Vežba 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 zapišete (engl. log) p o m o ć u m e to d a klase java.util.logging. Iako je
zapisivanje d etaljn o o b jašn 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 grešaka pomoću 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 tatičn 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 ičn o im e n o m p ak eta i klase u k o jim a je n asta la greška)
koji svoj izlaz šalje u System.err. U za p isn ik se najlakše 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 bačen , 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 ože 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, češće se dešava d a hvatate i zapisujete tu đ e izuzetke, pa
p o ru k u za zap isn ik m o ra te d a generišete 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 uhvaćenih 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 ožete 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 grešaka pomoću 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( ) liči na m e to d u to S trin g ( ) za kla-
se izuzetaka.
Pošto 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 vaše pak ete m o žd a
neče ni p rim e titi ulepšavanje, p o što če v ero v atn o očekivati generisanje izuzetka i n išta vi-
še. (Tako se k oristi većina izuzetaka u Javinim b ib lio tek am a.)
V ežba 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 ažite d a o n e rade.
Vežba 7: ( 1) Izm enite vežbu 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 vašu m e to d u o izuzecim a
koje o n a m o že da generiše. To je učtivo, 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 sprečilo 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 učtivo kažete p ro g ra m e ru
k lijen tu koje izuzetke g eneriše 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 vaše 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 napišete:

void f () { / / . . .

to znači da m e to d a ne generiše 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 lažete u specifikaciji izuzetka. U k oliko k o d u n u ta r vaše 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 štiće v a m d a m o ra te
o b ra d iti izu ze tak ili specifikacijom izu zetk a n azn ačiti d a ga vaša m e to d a m o ž e g enerisati.
P o d ržav 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 prevođenja.
O je d n o j stvari m o žete lagati: m o žete tv rd iti d a se izu zetak generiše, iako se to zap rav o
ne dešava. P revodilac vam veru je i p rim o ra v a k o risn ik e d a s v ašo m m e to d o m ra d e kao da
o n a zaista generiše taj izuzetak. T im e se p o stiže 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 erišete izuzetak n e m en ja ju ć i p o sto jeći 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.
Vežba 8: (1) N apišite klasu s m e to d o m koja g eneriše izuzetak tip a d efin isan o g u vežbi 4.
P ok ušajte da je prevedete bez sp ecifik a đ je izuzetka i pog led ajte šta će vam reći 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 oguće 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 biće uhvaćen 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 ležn 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 ošto 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 važne, 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 grešaka pomoću izuzetaka 357

V raća 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 položaj n astan k a izuzetka n a stek u izvršavanja. Stek izvršava-
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 greške, 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 iće m o zašto su p o d rž a n e dve vr-
ste to k a ).
ThrowabIe fillInStackTrace()
In fo rm a c ije o tekuće m sta n ju slojeva steka beleži u o b je k tu tip a Throwable. O vo je ko-
risn a funk cija u slučajevim a kad a aplikacija p o n o v o izbacuje p o sto je ć u grešku ili izu zetak
(u sk o ro će b iti više reči 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 korišćenje 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 više info rm acija, je r je svaka n a d sk u p


p re th o d n e .
Vežba 9: (2) N ap rav ite tri nova tip a izuzetaka. N ap rav ite klasu s m e to d o m koja generiše
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.

Položaj nastanka izuzetka na steku izvršavanja


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 vraća 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 (tačk u u kojoj je ovaj o b jek at izuzetka n a p rav ljen i bačen ). 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 položaju nastanka izuzetka
// na steku izvršavanja.

p u b lic c la s s KoJeZvao {
s t a t i c void f ( ) {
// G eneriši izuzetak da bi na stek iz vrš a v a n ja upisao njegov
// položaj 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 grešaka pomoću 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 ispišete ceo StackTraceElement


koji sadrži d o d a tn e p o datk e.

Ponovno generisanje izuzetka


P o n ek ad ćete poželeti d a p o n o v o generišete izuzetak koji ste u p rav o uhvatili, n a ro č ito ako
k o ristite klasu Exception za hvatan je svih izuzetaka. Pošto 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-


dećem 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 višem o k ru ž e n ju koji hvata o d re-
đ en i tip izuzetka m ože da ih izdvoji iz tog objekta.
A ko p o n o v o generišete tekući 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
generišete. N ove inform acije o sta n ju steka m o žete da up išete p o zivan jem m e to d e fillln-
StackTrace( ) koja vraća objek at izuzetka, n a p rav ljen tako što su in fo rm acije o tek uće 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 oguće je p o n o v o gen erisati izu zetak koji je različit o d o n o g koji je uhvaćen. To p ro -
uzro k u je isti efekat k ao k o rišćen 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 grešak<i, oomoću izuzetaka 361

//: izuzeci/PonovnogenerisanjeNovog.java
// Ponovno generisanje objekta koji se razlikuje or5 '^ivaćenog.

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("izbačen iz f ( ) " ) ;
}
public static void main(String[] args) {
try {
try {
f();
} catch(IzuzetakJedan e) {
System.out.println(
"Uhvaćen u unutrašnjem bloku try, e.priri: ackTrace()");
e.printStackTrace(System.out);
throw new IzuzetakDva("iz unutrašnjeg bloka uy");
}
} catch(IzuzetakD va e) {
System .out. pri n tln (
"Uhvaćen 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 ( )
Uhvaćen u unutrašnjem bloku t r y , e . p rin tS tack T race(
IzuzetakJedan: izbačen iz f ( )
at PonovnogenerisanjeNovog.f(Ponovnogenerisan .N ovog.java:15)
at PonovnogenerisanjeNovog.main(Ponovnogene. .jeNovog.java:20)
Uhvaćen u spoljnom bloku t r y , e .p rin tS ta c k T ra c e ()
IzuzetakDva: iz unutrašnjeg bloka t r y
at PonovnogenerisanjeNovog.main(Ponovnogenc .njeNovog.java:25)
* ///:-

K onačan 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 pšte 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 ičk o j m em o riji, pa ih sakupljač
sm eča a u to m atsk i briše.
362 Misliti na Javi

Ulančavanje 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 izgubivši p o d a tk e o p rv o m
- to se naziva ulančavanje izuzetaka. P re objavljivanja JD K -a 1.4, p ro g ra m e ri su sam i m o -
rali da p išu k o d koji će očuvati 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 zadržavate položaj prv o g
izuzetka na steku, iako p rav ite i g en erišete 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 greške), 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 izvršavanja:

//: iz u zeci/D in am ick aPo lja.java


// Klasa koja sebi dinamički dodaje p o lja .
// Ilu s t r u je ulančavanje 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 grešaka pomoću 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 . Dodaćemo 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 proširenim 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) {
// Većina izuzetaka nema konstruktor k o ji bi primio argument "cau se".
// U tim slučajevim 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 sađrži 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 ože 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 ači p ostojeće, tako nazv an o
p olje ili će n a p ra v iti novo, i staviti u njega vašu v red n o st. U koliko joj p o n e sta n e p ro sto ra ,
d o d aće 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, generisaće 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 grešaka pomoću 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 greška, 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 saznaćete više 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 išete u toS tring( ) koja o b u h v a ta k ru že n je
u petlji, kao ovde.
Vežba 10: (2) N aprav ite klasu s dve m eto d e, f ( ) i g ( ). U m e to d i g ( ) g en erišite 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 enerišite d ru g aćiji 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 ( ).
Vežba 11: ( 1) P o n ovite p re th o d n u vežbu, 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 opšte
vrste o b jek ata tip a Throwable (vrsta o b jek ta = n asleđivanje iz). Error p red stav lja sistem -
ske greške i greške 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
izvršavanja. 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 ačin 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 različitim
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 . Takođe, 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 rža ti i sopstve-
ne izuzetke. V ažno je razu m eti šta su izuzeci i šta se s n jim a m ože 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 očigled 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 ršk e d ru g im b ib lio tek am a kao što su util, net i io, što se m ože 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 slučaj 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). Srećom , 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 izvršavanja 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 suvišni, 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 eriše 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
savršen p rim e r nasleđivanja: uspo stav lja p o ro d ic u tip o v a d elim ičn o zajedničkih o so b in a
i p o n a šan ja. Takođe, n ik ad a n ije p o tre b n o d a pišete 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 asleđeno g o d RuntimeException), p o što su to neprovereni izuzeci (engl. unchecked ex-
ceptions). Pošto RuntimeException u k azu je n a greške 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 tražite 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-
nerišete n e k u v rs tu ovakvog izuzetka.
Šta se dešava ako ne u h v atite ovakav izuzetak? Pošto prev o d ilac ne zahteva specifika-
ciju takvog tip a izuzetaka, zvuči 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 uhvaćen. Da b ism o videli šta će se desiti u to m
slučaju, p o g led ajm o sledeći 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 slučaj, p ošto prevodilac ne zahteva specifikaciju ovakvih vrsta izuzetaka. Izlaz u si-
stem skom to k u grešaka (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 grešaka pomoću 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 uhvaćen, 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 grešku u p ro g ra m ira n ju :
1 . G reška 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 reška 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 znači 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 slučaju - olakšavaju
p o s tu p a k p ro n a la žen ja i o tk la n jan ja grešaka.
Z anim ljivo je p rim e titi d a se Javina teh nik a o b rad e izuzetaka ne m ože klasifikovati kao
alatka specifične n am en e . O n a je projek to vana za rad s n ezgo dn im greškam a to k o m izvr-
šavanja koje se dešavaju usled delovanja činilaca van d om ašaja vašeg koda, ali je o d presu d -
n e važn o sti i za o d re đ e n e vrste p ro g ram ersk ih grešaka koje prevodilac n e m ože da otkrije.
Vežba 12: (3) Izm en ite p ro g ra m unutrasnjeklase/Sekvenca.java tako d a generiše od g o -
v araju ći izu ze tak ako p o k u ša te d a u m e tn e te previše elem enata.

Čišćenje odredbom fin ally


C esto p ostoie delovi k o d a koje želite da izvršite 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 ičn o slučaj 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 eća). 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 generišu 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 dešavaju
}

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 izvršava, p o k re n ite sledeći 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 uvećavanje 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 izvršava, bez o b z ira n a to da li se izuzetak


generiše ili ne.
Na osno v u ovog p ro g ra m a m o žete zaključiti kako da se iz b o rite s čin jen ico m d a izu-
zeci u Javi n e dozvoljavaju vraćan 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 ožete da uvedete uslov koji m o ra da b ud e
zadovoljen pre nego što se n astavi izvršavanje p ro g ra m a . M ožete da d o d a te i statički 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 ačin vaši p ro g ra m i m o gu da p o sta n u rob usniji.

Čemu služi odredba finally?


U jeziku koji n em a sakupljač sm eća ni au to m atsk e pozive d e stru k to ra ,5 o d re d b a finally je
važna zato što p ro g ra m e ru o m o g u ću je da g aran tu je o slo bađan je m em o rije, bez o bzira na
to šta se dešava u blok u try . M eđ u tim , Java im a sakupljač sm eća, pa o slo bađan 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 tačno
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 sličniji Javi) u m e da au to m atsk i un išti n ep o treb n e objekte.
Poglavlje 12: Obrada grešaka pomoću 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 ešto što nije
m e m o rija . To m ože b iti zatv aran je o tv o ren e d ato tek e ili rask id veze s m režo m , b risan je
nečega š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 ača iz sp o ljn o g sveta, kao
što je m o d e lo v an o u sledečem 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 generiše 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 isključen k ad a se završi izvršavanje 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 aćen, p a bi m e to d a pr.is-
k lju cen ( ) bila preskočena. P o m o ć u o d re d b e fmally, k o d za čišćenje m ožete 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 može da generiše 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 ešten a n a m esto gde će se sig u rn o izvršavati bez


o b zira na to šta se desi.
Ć ak i u slučajevim a kada se izuzetak ne u hvati u tek u ćem sk u p u blokova c a tc h , finally
će se izvršiti p re nego što m eh an izam za o b ra d u izuzetaka n astavi da traži b lo k za o b rad u
na sledećem 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 grešaka pomoću 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 (
"Uhvaćen 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
Uhvaćen 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 izvršavati 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 sledeće: u z o zn ačen break i ozn ačen continue, fmally u kida
p o tre b u za n are d b o m goto u Javi.
Vežba 13: (2) P ro m en ite vežbu 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 izvršava, čak i ako je g enerisan izuzetak NullPointerException.
Vežba 14: (2) Pokažite 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.
Vežba 15: (2) Pokažite 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 pomoću


rezervisane reči return
Pošto se blok fin ally uvek izvršava, m o g u ć je p o v ra ta k s više tačaka m eto d e , a d a se i dalje
m ože jem čiti da će v ažno čišćenje 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 ačka 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
Tačka 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
Tačka 1
Tačka 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
Tačka 1
Tačka 2
Tačka 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
Tačka 1
Tačka 2
Tačka 3
Kraj
O b avljan je čišć e n ja
* ///:-

Iz re z u ltata v idite da nije v ažn o gde se v raćate iz klase koja sadrži o d re d b u finally.
Vežba 16: (2) P rep rav ite p ro g ra m ponovnaupotreba/CADSystem.java tako da pokažete
da se p ra v iln o čišćenje jem či čak i kada se p o v ra ta k rezerv isan o m rečju return obavi iz
sred in e b lo k a try-finally.
Vežba 17: (3) P reprav ite p ro g ra m polimorfizam/Zaba.java tako d a p rav iln o čišćenje
jem či b lo k o m try-finally, i p o k ažite d a to rad i čak i kada se rezerv isan o m rečju 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 ažalost, im a p ro p u sta . Iako izuzeci ukazuju
na slabu tačk u u p ro g ra m u i ne b i n ik ad a sm eli da se ig n o rišu , 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 grešaka pomoću izuzetaka 373

// : izu zeci/Zag ub ljen aPoruka.java


// Kako se izuzetak može 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 važan 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 rilićn o o zb iljan n e d o sta -
tak, je r znači da izuzetak m ože p o tp u n o da se izgubi i to na n ačin koji je m n o g o teže
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 generiše pre nego što je prvi o b ra đ e n , sm a tra velikom greškom 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 generiše 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 ičan p o v ra ta k (p o m o ću rezervisa-
ne reči 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 bačeni izuzetak č in i nemim.
re tu rn ;
)
}
} / / / =-

A ko p o k re n e te ovaj p ro g ra m , v idećete d a ništa n e ispisuje n a izlazu, iako se generiše je-


d an izuzetak.
Vežba 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 trećim izuzetkom .
Vežba 19: (2) Rešite 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.

Ograničenja izuzetaka
Kada redefinišete m e to d u , m o žete da generišete 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 ičen je, p o što znači 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 generišu 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 generiše 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 generiše izuzetke koje prevodilac proverava
Poglavlje 12: Obrada grešaka pomoću 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 prilagođavaju osnovnoj k la s i:
// ! void w a lk () throws PopFoul { } //Greška tokom prevodenja
// N IJE DOZVOljENO dodavanje izuzetaka
// postojećim 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 može dodati:
p u b lic void ra in H ard () throws RainedOut { }
// Možete o d lu č iti da uopšte ne g en erišete izuzetke,
// čak i ako to radi osnovna klasa:
p u b lic void e v e n t() {}
// R edefinisane metode mogu da generišu 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 dešava 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 važi 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 sadrži 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 erišu n o v tip izuzetka, Rai-
nedO ut. Kada klasa Storm yInning nasledi Inning i realizuje Storm , vid ećete d a m eto d a
event( ) interfejsa Storm ne inože 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, inaće 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 eriše so p stv en e izuzetke.
O g raničen 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 ože da generiše šta go d želi, b ez ob zira n a to šta generiše k o n stru k to r osnov-
ne klase. M eđ u tim , p ošto 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
deklariše 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 ože da u hvati izuzetke koje je g en erisao k o n stru k to r
osno v n e klase.
StormyInning.walk( ) neće b iti prev ed en zato što gen eriše izuzetak, što nije slučaj s
m e to d o m Inning.w alk( ). Kada bi to bilo d ozvoljeno, m ogli b iste da n ap išete 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 generiše 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 generiše izuzetke. Slična logika se p rim e n ju je i na m e to d u atB a t( ) koja ge-
neriše izuzetak tip a PopFouI izveden iz izuzetka tip a Foul koji g eneriše verzija m e to d e at-
Bat( ) o sn o v n e klase. Ako n eko nap iše 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. Pošto 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 grešaka pomoću 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 sledeće: 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 specifični 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 iče 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 sledeće: 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
znači 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 nasleđivanja, 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 rečim a, „interfejs specifikacije izuzetka" za o d re d e n u m e to d u m ože d a se suzi to -
k o m nasleđivanja i redefin isan ja, ali se neće 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 nasleđivanja.
Vežba 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 erišu . 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 ažn o d a se uvek pitate: „Ako n a sta n e izu-
zetak, hoće li o b jek at biti p ra v iln o počišćen?“ U većini slučajeva 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 ože d a izvodi neke o p eracije, npr. o tv a ran je d ato te k a , koje se n e b rišu sve d o k kori-
sn ik ne završi rad sa o b je k to m i pozove sp ecijaln u m e to d u za čišćenje. A ko generišete izu-
zetak u n u ta r k o n stru k to ra , čišćenje se m o žd a neće p ra v iln o izvršiti. To znači 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.
Pošto 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 rešenje. Ipak,
nije toliko jed n o sta v n o , p o što fin ally svaki p u t izvršava k o d za čišćenje. 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 izvršavanja, to m ože zn ačiti da nije usp eo d a n apravi
deo o b jek ta koji će biti počišćen u b lo k u finally.
U sledećem 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 reči 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 korišćenje:

//: izuzeci/lllazn aD atoteka.java


// O b ra tite pažnju 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 slična ograničenja kojim a se zahteva d a izuzeci izvedenih m eto da
b u d u isti kao izuzeci koje generiše m etoda osnovne klase, ili izvedeni iz njih. To je jed ini slučaj kada
je C + + zaista u stan ju da proverava specifikacije izuzetaka toko m prevođenja.
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 generiše izuzetke
} catch(FileNotFoundException e) {
System .o u t.p rin tln ("N isam našao " + 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 () neuspešan");
}
throw e; // G eneriši 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 () neuspešna");
}
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 spešno");
} catch(IO Exception e2) {
throw new RuntimeException( " i n . c l o se() neuspešna");
}
}
} ///= -

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 enerisaće izuzetak File-
NotFoundException. Taj izuzetak se m o ra u h v atiti zasebno, je r je to je d in i slučaj 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 ože d a g eneriše izuzetak FileNotFoundException.
Poglavlje 12: Obrada grešaka pomoću izuzetaka 379

U to m slučaju, m ožd 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 eneriše 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 itičastih zagrada. N ako n
izvođenja lo k aln ih o peracija, p o n o v o se generiše izuzetak. To je u red u , po što k o n stru k to r
nije b io uspešan, 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 slučaju zatvarala k ad g o d se izvrši 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 raća objek at klase String koji sadrži sledeći red datoteke. O n a p o -
ziva m e to d u readLine( ) koja m ože d a generiše izuzetak, ali se taj izuzetak hvata, pa citaj-
R e d ( ) n e generiše 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 ičn o i isti (ili drugačiji) izuzetak p ro sled iti dalje, ili ga valja p ro sle d iti bez ikakve o brade.
Prosleđivanje, kada je odgovarajuće, 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 grešku.
K orisnik m o ra da pozove m e to d u ciscenje( ) k ad a završi s ko rišćen jem o bjekta klase
UlaznaDatoteka. N a taj n ačin 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 završite s k o rišćen 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 čišćenje, ne m ožete 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 čišćenje, osim b risan ja m em o rije, ne izvršava 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 čišćenje.
N ajbezbedniji način korišćenja klase koja m o že g en erisati izuzetak to k o m k o n stru k c i-
je i koja zahteva čišćenje 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


// Zajemčeno 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() uspešno
* ///:-

Pažljivo 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 počišćen, 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 čišćenje 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 neće izvršiti ako k o n stru k c ija n e u sp e, a izvršava se uvek k ad a k o n stru k c ija uspe.
O vaj o pšti n ač in čišćenja 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 generiše 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 čišćenje,
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 može 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 može 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 grešaka pomoću izuzetaka 381

// Deo 2:
// Ako k o n stru k cija ne može da ne uspe, objekte možete da grup išete:
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 može 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 počišćena
T r e b a Je P o c is titi 3 počišćena
T r e b a Je P o c is titi 2 počišćena
T r e b a Je P o c is titi 5 počišćena
T r e b a Je P o c is titi 4 počišćena
* ///:-

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 ože 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 čiščenje.
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 ože d a n e u sp e i koji zah-
tevaju čiščenje. P raviln a o b ra d a ove situacije znači pravljenje krša, 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 čišćenje.
R užnoća k od a za o b ra d u izuzetaka u o v o m slučaju, 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 oguće.
382 Misliti na Javi

P ovedite ra č u n a o sledećem : ako ciscenje( ) m o že d a g eneriše 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 pažljivo d a ra z m o trite sve m o g u ć n o sti i
d a o bezbedite svaku o d njih.
Vežba 21: (2) P okažite d a k o n s tru k to r izvedene klase n e m ože d a hvata izuzetke koje ge-
neriše k o n stru k to r o sn o v n e klase.
Vežba 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 završi k o n stru k c iju i ta d a g en eriše izuzetak. U m e to d i m a in ( ) napišite k o d koji pravil-
n o štiti o d takvog n eu sp eh a.
Vežba 23: (4) U p re th o d n o j vežbi, 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
čišćenje kao o b jek at član, n a k o n čega k o n s tru k to r generiše izuzetak, a o n d a p rav i d ru g i
o b jek at član koji zahteva čišćenje. N ap išite 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 ažite d a ste se obezb ed ili o d svih m o g u ć ih n eu sp eh a.
Vežba 24: (3) Klasi N euspesanK onstruktor d o d a jte n e k u m e to d u ciscenje( ). N apišite
k o d koji p ra v iln o u p o treb ljav a tu klasu.

Pronalaženje sličnih izuzetaka


Kada se generiše izuzetak, sistem za o b ra d u izuzetaka tra ž i ,,najbliže“ 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 alažen je sličnih izuzetaka ne zah tev a d a izu zetak i b lo k za o b ra d u savršeno odgo-
v araju je d a n d ru g o m . O b jek tu izvedene klase o d g o v araće 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 tačan 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 grešaka pomoću izuzetaka 383

Izu zetak tip a Kijanje uhv atiće 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 rečim a,
catch (Dosada e) u h vatiće 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 više izv edenih izuzetaka, k o d p ro g ra m e ra kli-
je n ta neće 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 grešku, p o što vid i d a o d re d b a za h vatanje Kijanje nije d o stu p n a .
Vežba 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 eneriše 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 eneriše 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 naviše 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 ogućavaju da na-
p u sti izvršavanje 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 izvršavanje više nije m oguće ili poželjno.
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 ućih greška
izazvanih svakim p ozivom svake funkcije bio previše teg o ban , pa ga p ro g ram eri n isu sle-
đili. Stoga su greške ignorisali. Vredi p rim etiti da je olakšavanje p ro g ram ira n ja o b ra d e
grešaka bilo glavna m otivacija za izum izuzetaka.
Jedna o d v ažnih 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 važn 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
grešaka o d m a k n e o d tačke gde je greška 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 vašeg k o d a neće b iti z atrp a n logikom za o b ra d u
grešaka, pa će b iti m n o g o razum ljiviji i lakše će se održavati. O b ra d a izuzetaka o b ičn o
sm a n ju je o b im koda za o b ra d u grešaka, je r je d a n b lo k za o b ra d u m ože d a o b ra d i greške
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 grešku. To
p ro u z ro k u je p ro b le m „izaziva oštećenja ako se p ro g u ta “:

try {
II . . . radi se nešto 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 ošto vas p rev o dilac prisiljava d a o d m a h p išete k o d za o b ra d u izuzetka, izgleda k ao d a je
ovo najlakše rešenje, 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
,,rešio“ 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 i—kako
i treb a - u više 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 rešite.
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 išta 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 oči. M islim da je razlog za je d n o o d tih m išljenja 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 statički 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 statička p ro v e ra tip o v a uvek najb o lji o d g o v o r na većinu
p ro b lem a. Pokušaću da vam p ren esem m ali d eo evolucije m o g stan o v išta. P o su m n jao
sam u apsolutnu v re đ n o st statičke p rovere tipova; ja sn o je da je o n a najčešće 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 netačni. 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 slična 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 grešaka koji će im ati
više 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 arđizacije. 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 tiče iz C LU -a. M e đ u tim , tad a su posto jali i d ru g i
jezici koji su p o držav 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 grešaka pomoću 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 greške p rijav lju ju u p ro la zu sledeći:
. 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 teško č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 spreči 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 kažu:
„ ... 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
nečitljivim program im a u kojim a se izrazi p rekid a ju blokovim a za obradu.“
Sledeći p ristu p C LU -a p rilik o m p ro jek to v an ja C + + izuzetaka, S tro u stru p kaže d a je
cilj b io sm an je n je količine k o d a p o tre b n o g za o p o ra v a k o d grešaka. M islim d a je o p azio
kako p ro g ra m e ri n a C -u o b ičn o n e p išu k o d za o b ra d u grešaka, p o što su k o ličin a i položaj
to g k o da bili zastrašuju ć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 -
rišu greške 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 išu „ d o d a tn i" k o d koji
inače 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 grešaka, ko-
ličin 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 ože reći: „O vaj izu ze tak je n asta o u
m o m k o d u ; ti ga obradi". Ali m ože reći i: ,,Ja ću ig n o risati ovaj izuzetak koji m ože 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 ignorišem 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 erišu 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 generiše 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
izvršavanja. U koliko se generiše izuzetak koji krši specifikaciju izuzetaka, C + + p ro g ra m
će pozvati fu n kciju u n e x p e c te d ( ) (neočekivan) 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 ačin a
u p o tre b e g eneričk 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 (očigledno in-
spirisani specifikacijama izuzetaka u C++-u i činjenicom da se C++ programeri obično
ne bakću s njima). Međutim, taj eksperiment nije ponovljen ni u jednom kasnijem jeziku.
Drugo, provereni izuzeci izgledaju kao očito korisni u uvodnim primerima i malim
programima. Rečeno je da se suptilne razlike pojavljuju kada programi postanu veliki.
Naravno, do tolike veličine obično ne dolazi preko noći; 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 „teško upravljivi". Smatram da to može biti
posledica i preteranog proveravanja tipova; konkretno, provere izuzetaka.
Izgleda da je veličina programa značajna. To je problem, pošto se u većini rasprava za
ilustraciju upotrebljavaju mali programi. Jedan od projektanata C#-a kazao je sledeće:
„Ispitivanje malih programa navodi na zaključak da zahtevanje specifikacija izuzetaka
može povećati produktivnostprogramera i kvalitet koda, ali iskustvo s velikim softverskim
projektima nameće drugačiji zaključak - smanjuje se produktivnost a kvalitet koda se
povećava malo ili nimalo.“8
Autori CLU-a su kazali sledeće o neuhvaćenim izuzecima:
„Stnatrali smo da tiije realno zahtevati od programera da piše blok za obradu u situaci-
jam a kada se ne m ožepreduzeti ništa smisleno.“9
Kada objašnjava zašto deklaracija funkcije bez specifikacije znači da ona može generi-
sati bilo koji izuzetak, a ne da uopšte ne može generisati izuzetak, Stroustrup kaže:
,,To bi zahtevalo specifikacije izuzetaka za gotovo sve funkcije, predstavljalo bi značajan
uzrok ponovnog prevođenja i sprečilo bi saradnju sa softverom napisanim na drugitn
jezicima. To bi navclo programere da sabotiraju tnehanizme za obradu izuzetaka i da pišu
maskirni kdd koji sakriva izuzetke. Stoga bi Ijudi koji nisu uočili izuzetak stekli lažno
osećanje bezbcdnosti “w
Vidimo da se baš takvo ponašanje - sabotiranje izuzetaka - dešava s proverenim izu-
zecima u Javi.
Martin Fovvler (autor knjiga UML Distilled , Refactoringi Analysis Patterns) napisao mi
je sledeće:
,,...sve u svemu, smatram da su izuzeci korisni, ali Javini provereni izuzeci više koštaju
nego što vrede."
Sada smatram da je važan doprinos Jave bio to što je objedinila model prijavljivanja
grešaka, tako da se sve greške prijavljuju pomoću 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 grešaka. Ali ako imate dosledno prijavljivanje pomoću izuzetaka, možete ih koristiti
ako hoćete, a ako ne, oni će se popeti na najviši 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, trećc izdanje (A d d iso n -W e sle y , 1997), s. 376.
Poglavlje 12: Obrada grešaka pomoću 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 grešaka, d o d a tn o pojačanje 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 statičk 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 više din am ičk i n ego statički, dovelo m e je d o stan o v išta 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 rešaka 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 rečim 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 ažno 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 izvršavanja.
P o v rh svega toga, sm an je n je o g ran ičen 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 ačajn o povećanje p ro d u k tiv n o sti. Štaviše, za k o m p e n z a c iju p re-
više ograničavajuće statičke d o d ele tipova, p o tre b n i su refleksija i generički 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 svetogrđe i d a ću tim e u n ištiti 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 većeg 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 ukaže n a greške to k o m
p rev o đ en ja, ali je još važnije shvatiti o g ran ičen 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 naći na http://M indV iew .net/B ooks/B etterJava, naglašavam 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 više p o m a ž u nego kada sve p retv orite u sin tak sn u p o g rešk u . Ne zaboravite:
„Dobar program ski je zik je onaj koji p om a že program erim a da pišu dobre programe.
N ijedan program ski je zik ne m ože sprečiti da se na n je m u p iš u loši program i.“'2
U svakom slučaju, n ez n a tn a je verovatnoća d a će prov eren i izuzeci ikada b iti izbačeni
iz Jave. To bi bila previše 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 osećaj za razm ere toga, g otovo sav Sunov softver m ože d a se izvršava
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 osećate 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 ože se i drugačije.

Prosleđivanje izuzetaka na konzolu


U jed n o stav n im p ro g ram im a, kakvi su m nogi u ovoj knjizi, najlakši način da sačuvate izu-
zetke, a da ne pišete go m ilu koda, jeste prosleđivanje izuzetaka iz m eto d e m a i n ( ) na ko n -
zolu. Na p rim er, ako hoćete 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 reči 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 generiše 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 {
// Prosledićemo 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 aža-
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 previše 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 ).
Vežba 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 jeće 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 pišete 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 pišete telo
običn e m e to d e pa pozivate d ru g u m e to d u i sh v atite sledeće: „N em a m p o jm a šta da rad im
sa ov im izuzetkom ovde, a neću 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 rešenje. 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 rešenje k ad a ho ćete da ,,isključite“ 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 ulanča-
vanja izuzetaka n e g ubite in fo rm ac ije iz o rig in a ln o g izuzetka.
Poglavlje 17: Obrada grešaka pomoću 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 pišete blokove try -c a tc h n iti specifikacije izuzetaka. M e đ u tim , i
dalje m ožete 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();
// Možete 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 možete 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( ) sadrži k o đ koji g eneriše izu-


zetke različitih 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 eriše 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 išite k o d u n u ta r b lo k a try. P očinjete o d svih izuzetaka za koje izričito zn ate da
se m o g u pojaviti iz k o da u v ašem b lo k u try - u o v o m slučaju, p rv o se hv ata NekiDrugi-
Izuzetak. N a k raju , hvatate RuntimeException i g en erišete (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 rešenje je da n ap ra v ite so p stv en u p otld asu
o d RuntimeException. N a taj n ačin ne m o ra te vi d a hvatate izuzetak, a m ože d a ga u hvati
ko b u d e hteo.
Vežba 27: (1) P repravite vežbu 3 tako da se izuzetak p re tv ara u RuntimeException.
Vežba 28: (1) P repravite vežbu 4 tako da n a m en sk a klasa izuzetaka nasledi RuntimeEx-
ception i pokažite da tad a prev o d ilac dozvoljava da izostavite b lo k try.
Vežba 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 ažite 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 kažite kako se m etod e
m og u prevesti b ez specifikacija.
Vežba 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 različitih 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. Rešite 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 grešaka pomoću izuzetaka 391

3 . Z ak rp ite grešku i nastav ite ra d b ez p o n o v n o g isp rob avan ja m etode.


4 . Izraču 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 generišete 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 generišete drugačiji izuzetak
k a višem o k ru žen ju .
7 . Z avršite p ro g ram .
8 . P ojed n o stav ite k o d . (A ko n ačin n a koji generišete izuzetke još više k o m p lik u je kod,
o n d a ga nije lako i p o željn o k o ristiti.)
9 . Povećate 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 grešaka, a d u g o ro č n o u laganje u sn ag u aplikacije.)

Sažetak
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, nećete
baš m n o g o postići. 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 sledeća: 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 rešite, 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 grešaka u v re m e izvršavanja, p ita m se koliko često se aspekat
„oporavka" realizuje, pa čak i koliko često je to u o p šte m oguće. Po m o m osećaju, 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 leži u njih o v o j fu n k ciji ,,prijavljivanja“. Č in jen ica d a u Javi
svegrešk 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 grešaka n a više različitih načina, a m o žete i da ih
u o p šte ne prijavite. D osledan sistem za prijavljivanje grešaka znači da se više ne m o rate
pitati: „Da li su m i negde p ro cu rile greške?“ n a k o n pisanja svakog parčeta k o d a (ukoliko
ne „ p ro g u ta te “ izuzetke, narav n o !).
Kao što ćete videti u sledećim p oglavljim a, u koliko rešite ovo p itan je - čak i ako to u ra-
d ite g en erišući 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 ožete
u sred sred iti na zanim ljivije i teže p ro b lem e.
R ešenja o d a b r a n ih vežbi 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 eđu 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 najčešće k o rišćen u klasu to g jezika, String,
k ao i neke n je n e p rateće 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 ećete 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 vraća p o tp u n o n o v
o b jek a t tip a String koji sadrži m o d ifik aciju . O rig in a ln i String se ostavlja n eizm en jen .
Pogledajte sledeći 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 fizičkom 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 idećem o da je p ro sleđ en a referenca naz-
vana s i da p o sto ji sam o d o k se izvršava telo m eto d e v e ls lo v a (). Kada se izvršavanje m e-
to d e v e ls lo v a () završi, lo k aln a referenca s iščezava. M etoda v e ls lo v a () vraća 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
vraća referen cu to g rezultata. Ali ispostavlja se da je referenca koju vraća 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 kažete:

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 ešto što će b iti izm en je-
no. O vo je važn a garancija, je r se zbog nje k o d lakše čita i razum e.

Poređenje preklapanja operatora + i StringBuildera


P ošto 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 ešto š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 tiče 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 znači d a je o p eraciji d a to d ru g o zn ačen 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 ušajte 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 sadrži ,,abc“ n ad o v ezan na
sadržaj 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 eće. 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 išta 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 dešava, gornji ko d m ožete prevesti unazad alatkom ja v a p koja
se isp o ru ču je kao deo razvojnog o kruženja 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 veća, p re k la p a n je o p e ra to ra bi u Javi b ilo m n o g o lakše 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 u’H'u'.l’vthon.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 ašine (JV M ). Ako prv i
p u t u živ otu v id ite asem bler, ne u p u šta jte se - važno je u očiti 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 slučaju, p revodilac pravi o bjek at tip a StringBuilđer 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 zaključite 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 ačin a : k oristeći 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, videćete (p o jed n o stav ljen ) k o d te
dve različite 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 ažn ju na redove 8: i 35: koji zajedno čine petlju. Red 8: sadrži n are d b u „integer
co m p are g reater th an o r equal to “ (celobrojnog p o red en ja veče ili jed n ak o ) o p eran a d a na
steku i skače na 38: kada petlja završi rad. Red 35: (n ared b a goto) je v raćanje n a p o četa k
petlje, red 5:. Važno je p rim etiti da se konstrukcija StringBuildera odigrava u n u ta rte petlje,
što znači 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 raća 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 veličinu 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 izraču n a na razb o rit
način . 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 išete n ešto kao append(a + " + c), um ešaće 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( ), najčešće ć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 završn 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 izvršavanje (v id eti poglavlje Paralelno izvršavanje) 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 brže.
Vežba 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
Pošto su Javini sta n d a rd n i k o n tejn eri, kao i sve d ru g e klase, nasleđeni iz klase Object, oni
sadrže 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 sadrže,
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 vaša m eto d a to S trin g ( ) ispiše 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 ispišete, d o b ićete 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 ispišete taj k o n te jn e r k ao što je o vde p rik a z an o . Z apravo
se dešava au to m a tsk a konverzija tip a u String. K ada kažete:

"Adresa objekta klase BeskonacnaRekurzija: " + th is

prevodilac vidi String za kojim sledi + i n ešto š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 ispišete ad resu o b jek ta u o v o m slučaju, rešenje 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 ežba 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), Početak i kraj podniza koji treba kopirati, Kopiranje objekata tipa char
getBytes() niz u koji treba kopirati, indeks mesta u ili byte u spoljni niz.
odredišnom nizu.
toCharArray() Pravi char(] sa znakovima koje
sadrži String
equalsf), String za poredenje. Ispitivanjejednakosti sadržaja dva
equalslgnoreCase[) znakovna niza.
Poglavlje 13: Znakovni nizovi 399

Metoda Argumenti, preklapanje Upotreba


compareTo|) String za poređenje. 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 traži. Rezultat je true ako String sadrži
argument.
contentEquals() Objekat tipa CharSequence ili String- Rezultat je true ako se argument
Buffer za poređenje. tačno podudara.
equalslgnoreCase() String za poredenje Rezultat je true ako su sadržaji jed-
naki; zanemaruje se veličina slova.
regionMatches() Pomeraj od početka ovog Stringa, drugi Rezultat tipa boolean pokazuje
String i pomeraj od njegovog početka, i da li se dato područje podudara.
dužina koja se poredi. Preklapanjem se
dodaje zanemarenje veličine slova.
startsWith() String s kojim eventualno počinje. Rezultat tipa boolean pokazuje
Preklapanje dodaje pomeraj (ofset) da li String počinje 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 početni indeks, Vraća -1 ako se argument ne
lastlndexOf() String. String i početni indeks. pronađe u ovom Stringu;
u protivnom, vraća indeks početka
argumenta. lastlndexOf( )
pretražuje unazad, od kraja.
substring() (takode Preklopljen: početni indeks; početni in- Vraća nov objekat tipa String koji
i subSequence()) deks + završni indeks. sadrži dati skup znakova.
concat( 1 String koji treba nadovezati. Vraća nov objekat tipa String koji
sadrži znakove originalnog Stringa
i zatim znakove argumenta.
replace() Stari znak koji se traži. novi znak s kojim Vraća nov objekat tipa String sa
ga treba zameniti. Može zameniti ijedan sprovedenim zamenama. Vraća
CharSequence drugim stari String ako ne nade ono što
treba da zameni.
toLowerCase() Vraća nov objekat tipa String
toUpperCase() sa svim malim, odnosno velikim slo-
vima. Vraća stari String ako nema
šta da izmeni.
trimf) Vraća nov objekat tipa String bez
belina na oba kraja. Vraća stari
String ako nema šta da izmeni.
valueOf|) Preklopljen: Object, char[], char[J i po- Vraća String koji sadrži tekstualni
meraj od početka i broj znakova, bool- prikaz argumenta.
ean, char, int. long, float, double
internf ) Pravi tačnojednu String referencu
za svakujedinstvenu sekvencu
znakova.
40 0 Misliti na Javi

V id ite d a svaka String m e to d a vraća n o v String ob jekat k ad a tre b a d a iz m e n i sadržaj


zn a k o v n o g niza. Im ajte u v id u i sledeće: u koliko sadržaj 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 ićem 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 priželjkivanih 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 eće 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 eću 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 izvršavanja, v re d n o st o d x se u m eće 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 više 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 nostalgičare 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 način:
Syste m .o u t.p rin tln ("R e d 1: [" + x + " 11 + y +
// Novi način:
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 slučaja 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 sadrži 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 pošalje 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("Kornjača %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 :
Kornjača Tom je na (0,0)
Kornjača Teo je na (4,8)
Kornjača Tom je na (3,4)
Kornjača Teo je na (2,5)
Kornjača Tom je na (3,3)
Kornjača Teo je na (3,3)
* ///:-

Sav izlaz za k o rn jaču 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 više izlazn ih lokacija, ali n ajk o risn ije
su PrintStream (kao gore),O utputStream i File. O n jim a čete sazn ati više u poglavlju Ja-
vin ulazno-izlazni sistem.
Vežba 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 slože-
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 veličinu 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
značenja za različite tipove. Za znako vn e nizove, sp ecifik ato r preciznost zadaje največi
broj znakova objekta tip a String koje treb a ispisati. Z a b rojeve s p o k re tn im zarezom , pre-
ciznost zađaje 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 previše, Java će rezu ltat zao k ru žiti, a ako ih im a p rem alo , d o d ać e p rateće nule. Pošto
celi brojevi n em aju razlom ljen deo, preciznost se n a njih ne m ože p rim e n iti, i đob ićete
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 rašak", 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

Džekov čarobni 4 4.25


Prin cezin graša 3 5.10
Kaša 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 riličn o sažetu 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 ovarajući razm aci.
V ežba 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


Najčešće ćete sretati sledeće konverzije:

Znakovi konverzije
d Celi broj (napisan decimalno)
c Unicode znak
b Logička 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 naučnoj notaciji)
X Celi broj (napisan heksadecimalno)
h K[juč za heširanje (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 izvrša-
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 logičke
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 ačije 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 eričk 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 logički.
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 išite najsloženiji m o -
gući 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 statičn 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 vraća 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 neuspešno");
} 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 neuspešno
* ///:-

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 vaše arg u m en te,
ali k o risteći 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, koristeći
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 učitavanje 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 raća celu d a to te k u u o b lik u n iza b ajto v a (byte).
V ežba 6: (2) N apravite klasu koja sad rži 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 ažite 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 uspešan). 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 ogućnosti.
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 složenih 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 hoćete. Iako je
sin taksa re g u larn ih izraza isp rv a teška, n jih o v sažet d in a m ič k i jezik m o žete u p o tre b iti za
rešavanje 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 način.

Osnove
R egularan izraz je način o p isivanja zn ak o v n ih nizova na u o p šte n n ačin , ta k o d a m ožete
reći: ,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 saopštili da isp red b ro ja m o že, ali n e m o ra biti znak m in u s, pišete m in u s i iza
njega zn ak pitanja:

_?

D a b iste opisali celi broj, kažete d a je to je d n a ili više 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 \\
znači: „ 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 značenje.“ U Javi, \\ znači: „U m ećem k osu c rtu u regularan
izraz, p a sledeći znak im a sp ecijaln o zn ačen 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 , pišete \\\\. M eđ u tim , za prelazak u novi red i
ta b u la to r piše se sam o jedna kosa crta: \n \t.
Poglavjje 13: Znakovni nizovi 409

+ o zn ačav a„jed an ili više 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 više cifara“, pišete:

-?\\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 treći p o čin je zn ak o m +, k jji e sam p o sebi leg itim an ,
ali ne o d g ovara našem 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 početi
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 znači logičko ili (O R ). D akle izraz

(-|W + )?

znači da ovaj d eo znak o v n o g niza m ože b iti - ili + ili (zbog zn ak a ?) n i je d n o ni dru go .
Pošto u reg u larn im izrazim a zn ak + im a sp ecijaln o značenje, 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 ičan 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 znači:
„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 sadrži s p e c ija ln e znakove
spl i t ( 11\\W+") ; // Znakovi k o ji ne mogu b i t i deo reči
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 ičn 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 ržati 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( ) sadrže \W , izraz za zn a k koji n e m o že biti d eo reči
(verzija n a p isa n a m alim slovom , \w , obeležava z n a k koji m ože b iti d eo reči). U o čite d a su
u d ru g o m slučaju u k lo n jen i znakovi in terp u n k cije. Treći p o ziv m e to d e sp lit( ) kazuje:
„slovo n i je d a n ili više zn akova koji ne m o g u b iti d e o reči“. 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 najvećeg đ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 ožete 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 više zn akova koji m o g u b iti d eo reči (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 rečju ,,located.“
D ru g o m izrazu od g o v ara bilo koja o d tri reči koje su razdvojene v ertik aln im c rta m a
(logičkim 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 idećete 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 ožete p o zivati m e to d e d a o n e obavljaju zam ene.
U slučajevim a kada se reg u larn i izraz u p o treb ljav a više 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.
Vežba 7: (5) K oristeći d o k u m e n tac iju klase java.util.regex.Pattern kao resu rs, n ap išite i
testirajte reg u la ra n izraz koji proverava d a li rečenica p o čin je velikim slovom i završava se
tačkom .
Vežba 8: (2) Pocepajte znakovni niz Cepanje.vitezovi ta m o gde se n a đ u reči ,,the“ ili ,,you.“
Vežba 9: (4) K oristeći 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 učite o re g u larn im izrazim a, m ožete 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 Vraćanje na početak tekućeg 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
način 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, vraćanje na početak tekućeg reda)
\S Bilo koji znak sem znakova za belinu (fA\s])
412 Misliti na Javi

Znakovi
\d Numerička cifra [0-9J
\D Bilo koji znak sem znakova za cifre [A0-9]
\w Bilo koji znak koji može biti deo reči [a-zA-Z_0-9]
\W Bilo koji znak sem znakova koji mogu biti deo reči [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 obeleživač.

Logički operatori
XY X, a potom Y
XIY X ili Y

(X) Grupa koju treba pronači. U nastavku izraza, /tu grupu za hvatanje označavate sa \i.

Izrazi za granice
A
Početak reda

$ Kraj reda
\b Granica reči
\B Granica koja ne može biti deo reči
\G Kraj prethodno pronadene podudarnosti

Kao p rim e r, svi sledeći 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 najteže shvatljiv reg u laran izraz, nego najjednostavniji
koji „završava p o sao “. K ada p o č n e te d a pišete 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 ičan 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 nastaviće d a tra ž i sve d o k n e p ro n a đ e
najveći 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 obeležava 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
podudarajući, 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 ećete 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 eriše 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 nađe
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 sprečavaju
vraćanje. M ožete ih u p o tre b iti k ako b iste sprečili 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 više puta
x+ X+? X++ X, jednom ili više puta

X(r>} X{n}? X{n}+ X, tačno 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 više 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
hoćete. 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 više 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 kaže: „ P ro n ađ i ab i iza njega je d n o ili više slova c.“ D a b iste p ro n ašli ceo znakovni
niz abc je d n o m ili više 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 ačiji 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, nećete se služiti 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 retražiti. 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 ožete 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, sledeća 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 više 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 aša 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 erišete 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 ešn o sti ili n eu p e šn o sti različitih 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 spešn 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 ešn 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 počev od njegovog početka.
416 Misliti na Javi

Vežba 10: (2) Za rečenicu „Java now has regular expressions“ odredite da li će sledeći iz-
razi pronaći neko podudaranje:

''Ja v a
\B reg .*
n.w \s+ h (a|i)s
s?
s*
s+
S {4}
S {1 ).
s{0 ,3 )

Vežba 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( ) može se upotrebiti za pronalaženje p o dudaranja više 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 reči. M etoda fin d () radi kao iterator, pošto se kroz ulazni
znakovni niz kreče unapred. M eđutim , drugoj verziji m etode fin d ( ) možete dati celobroj-
ni argum ent koji joj kazuje redni broj znaka za početak pretraživanja - ta verzija zadaje
vrednost argum enta kao mesto pretraživanja, 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( ) vraća broj grupa u uzorku objekta. G rupa 0 se ovde ne broji.
public String g ro u p ( ) vraća grupu 0 (ceo p o d u darni deo) iz preth o d n e operacije pro-
nalaženja podudarnosti (m etode fin d ( ), na prim er).
p u b lic String group(int i) vraća grupu datog rednog broja iz preth o d n e operacije pro-
nalaženja podudarnosti. Ako je pronalaženje p odudarnosti uspešno obavljeno, ali se data
grupa nije podudarila ni s jednim delom ulaznog znakovnog niza, vraća se null.
public int start(in t group) vraća indeks početka grupe pronađene u prethodnoj operaciji
pronalaženja podudarnosti.
p ublic in t e n d (in t group) vraća indeks poslednjeg znaka, plus jedan, grupe pronađene u
prethodnoj operaciji pronalaženja 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 početak pesme ,,Jabberwocky“ Luisa Kerola, iz knjige Alisa iza ogledala. Uzorak u
regularnom izrazu im a više grupa u zagradam a koje se sastoje od proizvoljnog broja znako-
va koji nisu beline (\S+) praćenog proizvoljnim brojem znakova koji jesu beline (\s+). Cilj
je uhvatiti tri poslednje reči u svakom redu; kraj reda označava $. M eđutim, uobičajeno po-
našanje je da se $ poredi s krajem celog ulaznog niza, pa regularnom izrazu m orate ekspli-
citno reći da obrati pažnju na znakove za prelazak u novi red u ulaznom nizu. To se postiže
indikatorom uzorka (?m) na početku niza (ubrzo ćemo razm otriti indikatore uzoraka).
Vežba 12: (5) Prepravite program Grupe.java tako da broji sve različite reči koje ne
počinju velikim slovom.

Metode start() i end()


Nakon uspešno obavljene operacije traženja podudaranja, m etoda s ta rt( ) vraća indeks
početka prethodno podudarnog dela, a e n d ( ) vraća indeks poslednjeg podudarnog znaka
plus jedan. Pozivanjem m etoda s ta r t( ) ili e n d ( ) nakon neuspešno obavljene operacije
traženja podudaranja (ili pre nego što je takva operacija započeta) 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 +
početak = "+ 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 ( ) početak = "
+ m .startO + " kraj = " + m .end());
if(m .m atches()) // re se t() n ije potrebna
d .is p is i ("matchesO početak = 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 ' početak = 11 kraj = 16
\w*ever
fin d () 'whenever' početak = 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' početak = 27 kraj = 35
\w*ever
f in d () ’w herever‘ početak = 27 k raj = 35
T\w+
f in d () 'T a rg a th ia n ' početak = 0 kraj = 10
lo o k in g A t() početak = 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' početak = 43 kraj = 48


ulaz : This fine ship, and this fine crew ...
T\w+
find() 'This' početak = 0 kraj = 4
lookingAt() početak = 0 kraj = 4
ulaz : Never give up! Never surrender!
\w*ever
find() 'Never' početak = 0 kraj = 5
find() 'Never' početak = 15 kraj = 20
lookingAtO početak = 0 kraj = 5
Never.*?!
find() 'Never give up !' početak = 0 kraj = 14
find() 'Never surrender!' početak = 15 kraj = 31
lookingAt() početak = 0 kraj = 14
m a t che s O početak = 0 kraj = 31
* ///:-

Vodite računa o tom e da će m etoda fin d ( ) pronaći regularan izraz bilo gđe u ulazu, ali
lookingA t( ) i m atch es( ) uspevaju sam o ako se regularan izraz p o d udara od samog
početka 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.
Vežba 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 utiču na ponašanje prilikom
traženja 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 sledećih konstanti klase Pattern:

Indikator prevođenja 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 ponašanje poredenja bez uzimanja u obzir veliči-
(?') ne slova |engl. case-insensitive matching) jeste da se porede samo
znakovi iz skupa US-ASCII. Ovaj indikator služi 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, možete
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 upućuje, ali u m iru ju


činjenicu da o soba koja izm išlja 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
nešto počinje da zam ara nakon nekoliko godina.
Poglavl'c ! 3: Zi lakovni nizovi 421

Indikator prevođenja Uticaj


P attern.CO M M EN TS U ovom režimu zanemaruju se bo'ine. f .'o kraja reda zanemaruju
(?x) se ugradeni komentari koji poč .■’ju y kom #. Ugradivanjem indi-
katora (?x) u izraz može se ukjjučru /im Unix lines.
P attern.DO TALL U režimu dotall, izraz . se podudctr~ - bilo kojim znakom, čak i s
l?sj graničnikom reda. Izraz . se poctr r /ano ne podudara s gra-
ničnicima redova.
Pattern.M ULTILINE U režimu multiline, izrazi A i $ , .. .„uju se s početkom 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 Poređenje bez uzimanja u obzir vtw slova, kada je uključeno
(?u) indikatorom CASE_INSENSITIVE, snrovodi se u skladu sa stan-
dardom Unicode. Podrazumevano i .l ašanje poređenja bez uzi-
manja u obzir veličine slova jeste đ... ~>orede samo znakovi iz
skupa US-ASCII. ,» .
Pattern.UNIX_LINES U ovom režimu se u ponašanju i?\:,: A i $ kao graničnik reda
(?d) priznaje samo \n

O d ovih indikatora naročito su korisni Pattern.CASE_IK IIV E, Pattem.M UL


TILINE i Pattern.COMMENTS (koji povećava jasnoću i/ili sava dokumentaciju).
Im ajte u vidu da se ponašanje većine indikatora m ože postu L-tanjem znakova u za-
gradam a, napisanih ispod indikatora u tabeli, u regularan • ^pred mesta na kojem
hoćete da aktivirate taj režim.
Uticaj navedenih i drugih indikatora m ožete 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 počinju rečima java, Java, JAVA
itd. Podudaranje se ispituje za svaki red višerednog skupa (podudaranje delova koji
počinju na početku niza znakova i slede iza graničnika svakog reda u nizu znakova).
O bratite pažnju 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, razgraničenih 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 ajvišePod nizova)

Ovo je podesno za cepanje ulaznog teksta n a delove koji im aju zajednički graničnik:

/ / : 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( ) ograničava broj podnizova u rezultatu.


Vežba 14: (1) Ponovno napišite program Prim erZaSplit uz koriščenje 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 važna, pošto omogućava 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).
Pomoću ove m etode možete program ski da rašćlanite grupe i napravite m oćne zamene.
appendTail(StringBuffer sbaf, String zamena) poziva se nakon jednog ili više 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 početku program a biva izvađen i obrađen 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 ");
// Pronađi 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 početku svakog reda
// zameni s nula razmaka. Mora se u k lj u č i t i režim 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 učitava pom oću klase TextFile iz biblioteke net.mindview.util


(njen kod će biti prikazan u poglavlju Javin ulazno-izlazni sistem). Statična m etoda
re a d ( ) čita celu datoteku i vraća je kao String. ulazUM se pravi tako da se podudara s ce-
lim tekstom (obratite pažnju n a zagrade za grupisanje) izm eđu 1*1 i !*/. Zatim se dva i više
razm aka svode na jedan razm ak i uklanjaju se svi razm aci s početka svakog reda (m ora se
uključiti režim m ultiline da bi se to uradilo u svim redovim a, a ne sam o na početku ula-
za). Te dve zam ene se obavljaju ekvivalentom (u ovom slučaju, podesnijim ) m etode
replaceA ll( ) koji je deo klase String. Pošto se svaka zam ena u p rogram u upotrebljava
sam o jedanput, to ne košta ništa više nego da se obavljalo p reth o d n o prevođenje u obje-
kat tipa Pattern.
replaceFirst( ) obavlja samo prvu zam enu koju pronađe. Sem toga, zamenski znakovni
nizovi u m etodam a replaceFirst( ) i replaceAU() mogu biti sam o literali, pa ako svaku za-
m enu hoćete još da obradite, od tih m etoda nećete imati koristi. U tom slučaju upotrebite
m etodu appendR eplacem ent( ) koja omogućava da tokom obavljanja postupka zamene
upišete proizvoljnu količinu koda. U prethodnom prim eru, bira se i obrađuje jedna grupa
g ro u p ( ) - u ovom slučaju, regularni izraz pronalazi samoglasnik (engl. vowel) koji biva
napisan velikim slovom - dok se pravi rezultujući sbaf. O bično se korak po korak obave
sve zam ene i zatim pozove appendTail( ), ali ako hoćete 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 ogućava i da uhvaćenu grupu naznačim o direktno
u zam enskom znakovnom nizu izrazom ,,$g“, gde g označava broj grupe. M eđutim , to je
podesno za jednostavniju ob radu i u p rethodnom program u ne bi dalo željeni rezultat.

Metoda reset()
Postojeći objekat tipa Matcher m etodam a re se t( ) možete 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 početak tekućeg niza.

Regularni izrazi i Javin ulazno-izlazni sistem


U većini dosadašnjih prim era regularne izraze sm o prim enjivali na statične znakovne ni-
zove. U narednom prim eru prikazan je jedan način prim ene regularnih izraza za pro-
nalaženje 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 pronaći.
Iz rezultata se vidi broj reda gde je pronađena podudarnost i položaj(i) podudarajućih
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 objašnjeno


u poglavlju Javin ulazno-izlazni sistem), koji redove datoteke učitava u ArrayList. To znači
da iteraciju kroz redove TextFile objekta m ožem o obaviti foreach sintaksom.
Iako sm o mogli da pravim o nov objekat tipa M atcher u n u tar petlje for, nešto 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 pretražuje m etodom fin d ( ).
A rgum enti ispitivanja (treći red program a) znače da se datoteka JGrep.java otvara za
učitavanje kao ulaz i da se u njoj traže reči koje počinju sa [Ssct],
O regularnim izrazima m ožete naučiti m nogo više 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
naći korisni podaci.
Vežba 15: (5) Prepravite JGrep.java tako da prim a indikatore kao argum ente (npr. Pat-
tern.CASEJNSENSITIVE, Pattern.MULTILINE).
Vežba 16: (5) Prepravite JGrep.java tako da kao argum ent prim a ime datoteke ili direk-
torijum a (ako se zada direktorijum , pretraživanje treba da obuhvati sve datoteke u nje-
m u). Uputstvo: generišite listu im ena datoteka naredbom :

F i l e [ ] datoteke = new F i l e ( " . ") .1 i s t F i l e s ( ) ;

Vežba 17: (8) Napišite program koji učitava datoteku Javinog izvornog koda (čije ime
zadajete na kom andnoj liniji) i prikazuje sve njene kom entare.
Vežba 18: (8) Napišite program koji učitava datoteku Javinog izvornog koda (čije ime
zadajete na kom andnoj liniji) i prikazuje sve doslovno (Iiteralno) navedene znakovne ni-
zove u kodu.
Vežba 19: (8) Na osnovu preth od ne dve vežbe, napišite program koji pretražuje Javin iz-
vorni kod i ispisuje im ena svih klasa koje se spom inju u tom program u.
Poglavlje 13: Znakovni nizovi 427

Leksičko analiziranje ulaza


D osad je učitavanje podataka iz datoteke čitljive Ijudima ili sa standardnog ulaza bilo re-
lativno teško. Uobičajeno rešenje bilo je da se učita red teksta, raščlani na lekseme (engl.
tokens) i potom jezički 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 imaćeš %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 imaćeš 27.
Moj omiljeni broj tipa double je 0.809015.
* ///.-

U polju ulaz koriste se klase iz paketa java.io koji neće biti zvanično predstavljen do
poglavlja Javin ulazno-izlazni sistem. Objekat tipa StringR eader pretvara znakovni niz u
tok podataka (engl. stream) koji se može čitati, i taj objekat se upotrebljava za pravljenje
428 Misliti na Javi

objekta tipa BufferedReader, pošto BufferedReader im a m etodu readL ine( ). Zato se ob-
jekat ulaz može č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
hoćete 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 pošto je m etoda sp lit( )
uvedena tek u J2SE1.4, pre se to m oralo raditi drugačije.
Klasa Scanner se koristi od Jave SE5. O na obavlja glavni deo posla oko leksičkog 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 imaćeš % 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 imaćeš 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 takođe biti objašnjen u poglavlju Javin ulazno-izlazni sistem),
objekat tipa InputStream , objekat tipa String ili u ovom slučaju objekat tipa Readable,
Poglavlje 13: Znakovni nizovi 429

što je interfejs koji je Java SE5 uvela za opisivanje „nečega š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 učitavanje ulaza, podelu na lekseme i jezičku analizu
kriju u raznim vrstam a m etoda ,,next“. O bična m etoda n e x t( ) vrača sledeću 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 znači da vraćaju rezultat tek kada
celokupna leksema postane dostupna na ulazu. Postoje i odgovarajuće m etode ,,hasNext“,
koje vraćaju true ukoliko je sledeća 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 ožete da ga ispitate ako treba.
Vežba 18: (2) N apravite klasu koja sadrži 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 raščlani na spom enuta polja. D odajte m etodu to S trin g ( ) i
pokažite da klasa ispravno radi.

Graničnici klase Scanner


Klasa Scanner podrazum evano cepa ulazne lekseme na m estim a razmaka, ali m ožete da
zadate i sopstveni uzorak graničnika 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 graničnici prilikom čitanja datog znakovnog niza upotrebljeni
sli zarezi (oko kojih može biti proizvoljna količina belina). Ista tehnika se m ože upotrebiti
za učitavanje datoteka u kojim a su podaci razgraničeni zarezima. Pored m etode
u se D e lim ite r() za zadavanje uzorka graničnika, postoji i d e lim ite r() - on vraća tekući
objekat tipa P attern koji se upotrebljava kao graničnik.
430 Misliti na Javi

Leksička analiza pomoću regularnih izraza


Pored traženja unapred definisanih prostih tipova, m ožete tražiti i sopstvene (korisnički
definisane) uzorke, što je podesno za analizu složenijih podataka. U narednom prim eru,
tražim o preteće podatke u zapisniku kakav vodi, recim o, zaštitna 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 drugačijim 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 traži u sledećoj


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 leksičku analizu obavljate pom oću regularnih izraza, vodite računa o tom e da se
uzorak traži samo u sledećoj ulaznoj leksemi, pa ako uzorak sadrži i graničnik, neće ni-
kada biti pronađen.
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 lakše i sažetije obavlja pom oću regularnog izraza ili klase Scanner. Pogledajte jed-
nostavno poređenje 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 oću regularnih izraza ili objekata tipa Scanner, znakovni niz m ožete podeliti na
delove i korišćenjem složenijih uzoraka - što bi bilo teško pom oću klase StringTokenizer.
Izgleda da m ožem o bezbedno reći kako je StringTokenizer zastarela.

Sažetak
Ranije je Javina podrška za obradu znakovnih nizova bila rudim entarna, ali u novijim iz-
danjim a jezika postoji m nogo sofisticiranija podrška usvojena iz drugih jezika. Sada je
podrška za znakovne nizove prilično potpuna, iako ponekad m orate paziti na efikasnost,
recimo prilikom upotrebe klase StringBuilder.
R ešenja o d a b r a n ih vežbi 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 izvršavanja (engl. ru n -tim e type identification, RTTI) omogu-
ćuje ustanovljavanje i upotrebu tipa objekta dok se progratn izvršava.

RTTI VAS OSLOBAĐA O G RA N IČEN 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 ogućuje pisanje veom a m oćnih program a. Potreba za prepoznavanjem tipa u
vrem e izvršavanja pokreće niz zanim ljivih, a često i složenih problem a u objektno orijen-
tisanom projektovanju, a otvara i osnovno pitanje: kako bi trebalo da izgleda struktura
program a.
Ovo poglavlje obrađuje m etode za otkrivanje inform acija o objektim a i klasama u vre-
m e izvršavanja. To se obavlja na dva načina: tradicionalnim otkrivanjem tipa tokom iz-
vršavanja, kada se pretpostavlja da su svi tipovi dostupni već tokom prevođenja koda, i
refleksivnim m ehanizm om , koji om ogućuje otkrivanje inform acija o klasama isključivo
tokom izvršavanja.

Potreba za prepoznavanjem tipa tokom izvršavanja


Posm atrajm o sada već poznat prim er hijerarhije klasa u kojoj se koristi polimorfizam. Ge-
nerički tip je osnovna klasa Oblik, a specifični izvedeni tipovi su Krug, Kvadrat i Trougao:

Ovo je tipičan dijagram hijerarhije klasa u kom e se osnovna klasa nalazi na vrhu, a iz-
vedene klase se račvaju niže. Uobičajeni cilj u objektno orijentisanom program iranju jeste
da kod radi s referencama na osnovni tip (u ovom slučaju Oblik), jer ako kasnije odlučite
da proširite program dodavanjem nove klase (kao što je Romboid, izveden iz klase Oblik),
to neće uticati na najveći deo koda. U ovom prim eru, m etoda crtaj() u interfejsu Oblik di-
namički je povezana, pa program er klijent treba da poziva m etodu crtaj() preko generičke
reference na Oblik. Metoda crtaj() se redefiniše u svim izvedenim klasama, a pošto je reč
o dinam ički povezanoj m etodi, ona će se ponašati na odgovarajući način čak i ako se po-
ziva preko generičke reference na objekat klase Oblik. To je polim orfizam.
Stoga obično pravite određeni objekat (Krug, Kvadrat ili Trougao), pretvarate ga u
opštiji tip O blik (zanem arujete specifični tip objekta) i koristite tu referencu tipa Oblik u
ostatku program a.
Poglavlje 14: Podaci o tipu 433

H ijerarhija Oblika se m ože 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 sadrži m etodu crtaj() koja indirektno koristi m etodu toStringO za
štam panje identifikatora klase pom oću prosleđivanja reference this m etodi Sy-
stem .out.println() (obratite pažnju na to da je m etoda toStringO deklarisana kao
apstraktna, da bi se njeni naslednici prim orali da je redefinišu i da b ise sprečilo 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 prosleđenog objekta da bi predstavila njegov sadržaj u objektu tipa String. Svaka
izvedena klasa redefiniše m etodu toStringO klase Object, pa crtaj() na kraju u svakom
pojedinačnom slučaju (polim orfno) štam pa nešto različito.
U ovom p rim eru, do svodenja naviše dolazi kada se oblik smesti u List<Oblik>. Pri-
likom svođenja naviše gubi se podatak o tom e da su objekti specifični tipovi klase Oblik.
Što se tiče 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 izvršavanja, pošto se u Javi u vrem e izvršavanja proverava pra-
vilnost svih konverzija tipova. Upravo to i jeste smisao prepoznavanja tipa u vreme
izvršavanja: tokom izvršavanja, prepoznaje se tip objekta.
U ovom slučaju, konverzija je sam o delimična: 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 prevođenja to osigurava kontejner i Javin sistem ge-
neričkih tipova, ali tokom izvršavanja obezbeđuje ih konverzija tipa.
Sada na scenu stupa polim orfizam , pa se kod koji se izvršava 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 najveći deo program a zna što m anje o specifičnim tipovim a objekata, odnosno
da radi sa opštom predstavom o porodici objekata (u ovom slučaju, Oblik). Takav kod će
se lakše pisati, čitati i održavati, a projekti će se lakše prim enjivati, razum eti i menjati. Zbog
toga je polim orfizam jedan od opštih principa u objektno orijentisanom program iranju.
M eđutim , šta će se desiti ako p ri p rogram iranju im ate poseban problem koji se najlak-
še rešava ako znate tačan tip generičke reference? Na prim er, pretpostavim o da želite
om ogućiti korisnicima da posebnom bojom istaknu sve figure određenog oblika? Tako bi
mogli da pron ađu 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 preskočite. To om ogućuje prepoznavanje tipa u vreme izvršavanja:
možete da zatražite od reference na Oblik da vrati tačan tip na koji ukazuje i tako izabere
i izdvoji specijalne slučajeve.

Objekat tipa Class


Da biste razum eli kako se u Javi odvija prepoznavanje tipa tokom izvršavanja, prvo m o-
rate da shvatite kako se u vrem e izvršavanja predstavljaju inform acije o tipu. To se postiže
pom oću specijalne vrste objekta koji pripada klasi Class, a sadrži inform acije o klasi. Za-
pravo, za pravljenje svih objekata ,,običnih“ klasa koristi se upravo objekat klase Class.
Prepoznavanje tipa u vreme izvršavanja, Java obavlja pom oću Class objekta, čak i kada
radite nešto p oput svođenja naviše. Klasa Class obezbeđuje još načina za upotrebljavanje
prepoznavanja tipa u vreme izvršavanja.
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 mašina (JVM) koja
izvršava program upotrebljava podsistem nazvan učitavač klasa (engl. class loader).
Taj podsistem može da obuhvati ceo lanac učitavača klasa, ali postoji sam o jedan pri-
mordijalni učitavač klasa koji je deo realizacije JVM-a. Prim ordijalni učitavač klasa, obič-
no s lokalnog diska, učitava takozvane klase od poverenja, m eđu kojim a su i klase Java
API-ja. U lancu najčešće nije po trebn o im ati dodatne učitavače klasa, ali ako imate po-
sebne potrebe (kao što su učitavanje klasa na poseban način za podršku aplikacija Web
servera ili učitavanje klasa preko mreže), onda postoji način da te klase angažujete.
Poglav < 14: Podaci o tipu 435

U JVM-u se sve klase učitavaju dinamiđd, prilikom prve upotre v .To se đešava kada program na-
pravi prvu referencu nekog statičnog člana te klase. Ispostavlja se da ie i konstruktor statična metoda
klase, iako se za njega ne piše rezervisana reč static. Zato se i pravljr ■>y; novog objekta te klase opera-
torom nevv smatra za referenciranje statičnog đana te klase.
Dakle, program napisan na Javi ne učitava se po tp u n o pro ncgo što počne izvršavanje,
nego se delovi program a učitavaju po potrebi. Po tom e se java razlikuje od m nogih tra-
dicionalnih jezika. D inam ičko učitavanje om ogućuje ponašanje koje je teško ili nem o-
guće postići u jezicim a sa statičkim učitavanjem , kao što je C-H-,
Učitavač klasa prvo proverava da li je učitan objekat tipa Class za taj tip. Ako nije, po-
drazum evani učitavač ldasa učitava ga pronalaženjem datoteke .c lass s tim im enom . (Ui
učitavač klasa, instaliran kao softverski dodatak, traži odgovarajuće bajtkodove u bazi
podataka, na prim er.) Tokom učitavanja bajtkodova klase, JVv.' noverava da li su bajtovi
oštećeni i da li sačinjavaju loš kod. (To je jedan od načina nak< ' : r;> <i postižebezbedan rad.)
Kada se objekat Class za taj tip učita 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 načina 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 pronašao 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 sadrže blok static koji se izvršava p ri njihovom pr-
vom učitavanju. Informacije se ispisuju da biste znali kada se učitava odredena klasa.
O bjekti se u m etodi main() prave izm edu naredaba za štam panje, da bi se lakše odredio
tren u tak učitavanja.
Vidite da se svaki objekat klase Class učitava tek kada zatreba, a inicijalizacija bloka
static obavlja se po učitavanju 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 ožete da dobijete referencu na njega i da radite s njom (to radi učitavač kla-
sa). Jedan od načina za dobijanje reference na objekat tipa Class jeste statična m etoda for-
Name() čiji argum ent tipa String sadrži tekstualno im e (pazite na pravopis, m ala i velika
slova!) određene klase čiju referencu hoćete. Ova m etoda vraća referencu na objekat tipa
Class, koja je ovde bila zanem arena; m etodu forName() pozivam o radi njenog spored-
nog dejstva, a to je učitavanje klase Zvaka ako nije već učitana. U postu p k u učitavanja
izvršava se statični blok klase Zvaka.
U p rethodnom prim eru, ako m etoda Class.forN am e( ) zakaže zato što ne može da
pronađe klasu koju treba da učita, generisaće izuzetak ClassNotFoundException. Ovde
sam o prijavljujem o problem i idem o dalje, ali u sofisticiranijim program im a mogli biste
pokušati da rešite problem u bloku za o bradu izuzetaka.
Kad god budete hteli da upotrebite podatke o tipu prepoznate u vreme izvršavanja, naj-
pre ćete m orati da pribavite referencu odgovarajućeg objekta Class. Jedan od podesnih
načina za to pruža m etoda Class.forN am e( ), zato što vam nije potreban objekat tog tipa
da biste dobili referencu klase Class. M eđutim , ukoliko već im ate objekat tipa koji vas za-
nim a, referencu klase Class m ožete pribaviti pozivanjem m etode getC lass( ) koja je deo
korenske klase Object. Ona vraća 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 eći 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 pronađem 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 nasleđuje 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 oću m etode forN am e( ) u odgovarajućem bloku try. Vodite
raćuna o tom e da m orate navesti p un o im e (uključujujući i im e paketa) u znakovnom
nizu koji prosleđujete 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 oću objekta tipa Class
m ožete saznati zaista sve o određenom tipu.
U m etodi m a in ( ) poziva se m etoda C lass.getlnterfaces( ); ona vraća niz Class obje-
kata koji predstavlja interfejse obuhvaćene u Class objektu o kojem je reč.
Ukoliko im ate objekat tipa Class, m etodom getSuperclass( ) m ožete ga pitati koja je
njegova neposredna natklasa. M etoda vraća referencu objekta tipa Class koju možete
ispitivati dalje. Dalde, u vrem e izvršavanja možete saznati celokupnu hijerarhiju klasa
svakog objekta.
M etoda new ln stan ce( ) klase Class jeste jedan od načina da se realizuje„virtuelni kon-
stru k to r“, koji om ogućava da kažete: ,,Ne znam tačno 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 prevođenja. I kada napravite nov prim erak
neke klase, vraća 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 izvršite 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-
dećete kako se objekti klasa prave dinam ički, pom oću bilo kog konstruktora, u Javinom
refleksivnom interfejsu za program iranje.
Vežba 1: (1) U program u Ispitivanjelgracaka.java, pretvorite podrazum evani konstruk-
to r klase Igracka u kom entar i objasnite šta se zbog toga dešava.
Vežba 2: (2) U program Ispitivanjelgracaka.java dodajte novu vrstu interfejsa. Dokažite
da se on ispravno tretira i prikazuje.
Vežba 3: (2) D odajte Romboid u program Oblici.java. Napravite jedan Romboid, svedite
ga naviše na Oblik, zatim ponovo naniže na Romboid. Pokušajte da svedete naniže na
Krug i vidite šta se dešava.
Vežba4: (2) Prepravite prethodnu vežbu tako da se naredbom instanceof tip utvrdi pre
svođenja naniže.
Poglavlje 14: Podaci o tipu 439

Vežba 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 slučaju ne rotira).
Vežba 6: (4) Prepravite program Oblici.java tako da ističe sve oblike određenog tipa, od-
nosno postavlja indikator u njim a. M etoda to S trin g ( ) svake izvedene potklase klase Ob-
Iik treba da pokaže da li je taj O blik ,,istaknut“.
Vežba 7: (3) Prepravite ProdavnicaSlatkisa.java tako da svaku vrstu pravljenja objekta
određuje argum ent koji se zadaje na kom andnoj liniji. Dakle, ako na kom andnoj liniji
piše „java ProdavnicaSlatkisa Bombon“, o nda se pravi sam o objekat tipa Bombon.
O bratite pažnju na to kako argum entom s kom andne linije određujete Class objekte koji
če biti učitani.
Vežba 8: (5) Napišite m etodu koja prim a objekat i rekurzivno ispisuje sve klase u njegovoj
hijerarhiji.
Vežba 9: (5) Prepravite prethodnu vežbu tako da se pom oću m etode Class.getDeclared-
F ields( ) prikazuju i podaci o poljim a klase.
Vežba 10: (3) Napišite program koji utvrđuje da li je niz elem enata tipa char p ro st tip ili
pravi objekat.

Literali klase
Java nudi još jedan način za dobijanje reference na objekat klase Class, a to je korišćenje
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 prevođenja
(te stoga ne m ora biti smešteno u blok try). Pošto se ne poziva m etoda forName(), isto-
vrem eno je i efikasnije.
Literali klase rade sa običnim klasama, ali i sa interfejsima, nizovim a i prostim tipovi-
ma. Pored toga, sve om otačke klase prostih tipova sadrže standardno polje TYPE. Ovo
polje daje referencu na objekat tipa Class za sve odgovarajuće 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

Preporučujem da koristite verzije class ako je to moguće, pošto 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. Učitavanje, koje obavlja učitavač klasa. O na pronalazi bajtkod (obično na disku na-
vedenom u sistemskoj prom enljivoj classpath, ali to ne m o ra biti slučaj) i od njih
pravi objekat tipa Class.
2. Povezivanje. U fazi povezivanja proverava se bajtkod klase, dodeljuje m em orija sta-
tičnim poljim a, i ako treba, razrešavaju sve reference na druge klase koje ova klasa
pravi.
3 . Inicijalizaciju. Ako postoji natklasa, ona se inicijalizuje. Izvršavaju se statični inici-
jalizatori i statični blokovi inicijalizatora.
Inicijalizacija se odgađa do prve reference na neku statičnu m etodu (konstruktor je
im plicitno statičan) ili na neko nekonstantno statično 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 pokreće 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) ;
// Pokreće 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 ) ;
// Pokreće 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 oguće“. 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 statična finalna vrednost „konstanta u vreme prevođenja“ kao što je
Initabela.staticFinal, ta vrednost se može pročitati a da klasa Initabela ne bude inicijali-
zovana. M eđutim , ako je neko polje statično i finalno, takvo ponašanje nije zajemčeno:
pristupanje polju Initabela.staticFinal2 nam eće inicijalizaciju klase, pošto ono ne može
biti konstanta u vreme prevođenja.
Ako neko statično 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 počne čitati, kao što vidite iz pristupanja polju Initabela2.staticnaNe-
Finalna.

Generičke reference klasa


Class referenca upućuje na neki Class objekat koji proizvodi instance klasa i sadrži kod
svih m etoda za te instance. Sadrži i statične delove te klase. Stoga Class referenca zapravo
pokazuje tačan tip onoga na šta upućuje: određenog objekta klase Class.
M edutim , projektanti Jave SE5 ugrabili su priliku da to naprave malo specifičnijim,
tako što su om ogućili da generičkom sintaksom ograničite tip Class objekta na koji
upućuje 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 bična referenca klase ne prouzrokuje upozorenje. M eđutim , vidite da se obična refe-
renca klase m ože dodeliti bilo kojem drugom objektu tipa Class, d o k se generička refe-
renca klase m ože dodeliti sam o svom deklarisanom tipu. Koriščenjem generičke sintakse
om ogučavate prevodiocu da sprovede još jed n u proveru tipova.
Kako biste to ograničenje m alo ublažili? N a prvi pogled, izgleda da biste mogli uraditi
nešto poput:

Class<Number> genericNumberClass = in t . c la s s ;

Ovo naizgled ima smisla jer se Integer nasleđuje od klase Number. Ali to nije ispravno,
zato što Integer Class objekat nije potklasa N um ber Class objekta. (Razlika vam je mož-
da previše tanana; razm otrićem o je pobliže u poglavlju Generički tipovi).
Da bih ublažio ograničenja prilikom korišćenja generičkih Class referenci, upotreblja-
vam džokera koji je deo Javinih generičkih tipova. Džokerski znak je ? i on naznačuje „bilo
šta“. Stoga običnoj Class referenci u gornjem p rim eru m ožem o dodati džokere i dobiće-
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 nespecifičnu referencu klase ne upotrebljavate slučajno
ili zbog neznanja, nego namerno.
Za pravljenje Class reference ograničene na određeni tip ili bilo koji njegov podtip,
kom binujte džokera i rezervisanu reč extends; tim e pravite ograničenje. Dakle, um esto da
kažete 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 enerička sintaksa se dodaje Class referencam a sam o da bi se proverili tipovi u vreme
prevođenja, pa ako negde pogrešite, saznaćete to nešto ranije. Ni sa običnim Class refe-
rencam a ne m ožete zastraniti, ali ako negde pogrešite, saznaćete to tek nakon izvršavanja
program a, što um e da bude nezgodno.
Evo prim era upotrebe generičke sintakse. U p rogram u se skladišti 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 računa o tom e da ova klasa m ora pretpostaviti kako svaki tip s kojim radi im a
podrazum evani konstruktor (onaj bez argum enata), inače ćete dobiti izuzetak. Za ovaj
program prevodilac ne generiše nikakvo upozorenje.
Zanimljiva se stvar dešava kada za objekte tipa Class upotrebite generičku sintaksu:
m etoda new lnstance( ) vratiće tačan tip objekta, a ne sam o tip Object kako ste videli u
program u Ispitivanjelgracaka.java. To je donekle ograničeno:

//: 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 tačan 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 običan t ip O bject:
Object obj = n ad .new ln sta n ce();
}
} ///= -

Ako dobijete natklasu, prevodilac će dozvoliti samo da kažete kako je referenca natkla-
se „neka klasa koja je natklasa klase Lepalgracka“, kao što se vidi iz izraza Class<? super
Lepalgracka>. Neće prihvatiti deklaraciju CIass<Igracka>. To izgleda pom alo čudno,
zato što getSuperclass( ) vraća osnovnu klasu (ne interfejs), a prevodilac u vrem e pre-
vođenja zna koja je to klasa - u ovom slučaju, Igracka.class, a ne sam o „neka natklasa od
Lepalgracka“. U svakom slučaju, zbog te neodredenosti, povratna vrednost m etode
nad.new lnstance( ) nije tačan 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 više 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 možete da upo-
trebite običnu konverziju tipa. To se najčešče događa kada pišete generički kod (što ćete
naučiti u poglavlju Generički tipovi) i uskladištili 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 našao sam sam o jedan slučaj gde je m etoda c a st( ) bila korišćena (u
com .sun.m irror.util.D eclarationFilter).
Jedna nova m ogućnost uopšte nije našla p rim enu u biblioteci Jave SE5: Class.asSub-
class( ). O na služi za konverziju objekta tipa Class u specifičniji tip.

Provera pre konverzije tipa


D osad ste videli sledeće načine za prepoznavanje tipa tokom izvršavanja:
1 . Klasična konverzija tipa, npr. (Oblik), koja tokom izvršavanja prepoznaje tip da bi
proverila ispravnost konverzije tipa ili generisala izuzetak tipa CIassCastException
ako je konverzija loša.
2. O bjekat tipa Class koji predstavlja tip objekta. O bjekat tipa Class se m ože ispitivati
tokom izvršavanja da bi se saznale korisne informacije.
U jeziku C ++ , ldasična konverzija tipa (Oblik) ne prepoznaje stvarni tip tokom izvrša-
vanja, već sam o saopštava prevodiocu da objekat posm atra kao novi tip. U Javi, koja pro-
verava tip, ovakva konverzija tipa često se naziva „svođenje naniže koje čuva tip“ (engl.
type-safe đowncast). Ime „svođenje naniže“ (engl. downcast) nastalo je zbog uobičajenog
izgleda dijagram a hijerarhije klasa. Ako je konverzija iz klase Krug u Oblik svođenje na-
više (engl. upcast), onda je svođenje iz O blik u Krug svođenje naniže. M eđutim , pošto
znam o da je Krug istovrem eno i Oblik, prevodilac dozvoljava đodelu uz svođenje naviše
bez ikakvih ograničenja. Prevodilac ne može znati, za dati Oblik, šta je taj O blik zapravo
m ožda je baš Oblik, ili podtip klase Oblik, kao što su Krug, Kvadrat, Trougao ili drugi
oblik. U vrem e prevođenja, prevodilac vidi sam o Oblik. Stoga prevodilac ne dozvoljava
dodelu uz svođenje naniže bez eksplicitnog korišćenja operatora konverzije tipa, da bi
m u saopštio kako vi im ate dodatne podatke na osnovu kojih znate tačan tip (prevodilac
će proveriti da li je to svođenje naniže razborito i neće dozvoliti svodenje naniže na tip
koji nije potklasa).
Postoji i treći oblik prepoznavanja tipa tokom izvršavanja u Javi. To je rezervisana reč
in stan c eo f koja proverava da li je objekat instanca određenog tipa. O na vraća rezultat
tipa boolean, pa je na sledeći način možete koristiti u logičkim 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 izvrši kon-
verziju u tip Pas. Kada nem a drugih inform acija o tipu objekta, važno je da se in stan ceo f
upotrebi pre svođenja naniže; u suprotnom , nastaće izuzetak tipa ClassC astException.
446 Misliti na Javi

O bično ćete biti usredsređeni na jedan tip (npr. da pretvorite sve trouglove u ružičaste),
ali pom oću rezervisane reči instanceof m ožete lako proveriti sve objekte. Pretpostavimo
da im am o porodicu klasa za opisivanje kućnih Ljubimaca (i njihovih vlasnika, što će nam
zatrebati kasnije). Svaka Jedinka u hijerarhiji im a id i opciono ime. Iako sledeće klase na-
sleđuju klasu Jedinka, ona je donekle složena, pa ćemo njen kod prikazati i objasniti u
poglavlju Detaljno razmatranje kontejnera. Kao što zapažate, u ovom trenutku i nije neop-
hodno da vidite kod klase Jedinka - treba sam o da znate sledeće: možete je napraviti sa
im enom ili bez im ena i svaka Jedinka im a m etodu id ( ) koja vraća 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 ( ) vratiće samo prosto ime tipa.
Ova hijerarhija klasa nasleđuje 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 način za slučajno generisanje različitih vrsta ljubim aca, i da bi nam
bilo lakše, za pravljenje nizova i Lista Ijubimaca. Da bi ova alatka mogla da se razvija kroz
nekoliko različitih realizacija, definisaćemo 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 nasumičnog 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( ) obraća se nekoj izve-
denoj klasi - ovo je varijanta projektnog obrasca Template Method (Šablonska metoda).
Pošto je zadato da tip klase bude „nešto izvedeno iz klase Ljubixnac“, m etoda new-
Instance( ) pravi objekat tipa Ljubimac bez konverzije tipa. nekiLjubim ac( ) nasumično
indeksira Listu i izabrane objekte tipa Class upotrebljava za generisanje nove instance te
klase pom oću 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 ožete da prouzrokujete dve vrste izuzetaka
koje su obrađene u odredbam a catch iza bloka try. Podsetim o se, im ena izuzetaka su re-
lativno jasna objašnjenja onoga što je zakazalo (IllegalAccessException označava kršenje
pravila Javinog m ehanizm a bezbednosti, u ovom slučaju podrazum evani konstruktor je
privatan).
Kada izvodite potklasu klase PravljenjeLjubimaca, jedino m orate da navedete Listu
tipova ljubim aca koju hoćete da napravite m eto dom nekiLjubim ac( ) i drugim m etoda-
ma. M etoda getTypes( ) obično vraća sam o referencu neke statične 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 hoćete da n ap ravite nasumično:
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 oću m etode Class.forN am e( ).
To može da prouzrokuje izuzetak ClassNotFoundException, što im a smisla jer joj pro-
sleđujete znakovni niz čija se validnost ne m ože proveriti u vrem e prevođenja. Pošto su
objekti tipa Ljubimac u paketu podaciotipu, prilikom navođenja im ena klase m ora se
navesti i ime paketa.
Da bi se napravila Lista objekata tipa Class za različite tipove, potreb n a je konverzija
tipova koja prouzrokuje upozorenje u vrem e prevođenja. M etodu lo a d e r( ) definišemo
zasebno i zatim sm eštam o u n u tar statičnog bloka za inicijalizaciju, zato što se anotacija
@SuppressWarnings ne m ože napisati direktno u n u ta r statičnog bloka za inicijalizaciju.
Za brojanje prim eraka klase Ljubimci treba nam alatka koja prati broj različitih tipova
prim eraka klase Ljubimci. Za to je Mapa savršena; ključevi su im ena tipova Ljubimaca,
a vrednosti Integeri koji odražavaju broj Ljubimaca. Na taj način m ožete reći: „Koliko
im a objekata tipa Hrcak?" Za brojanje prim eraka klase Ljubimci m ožem 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 ično popunjava niz pri-


m ercim a klase Ljubinici. Zatim se svaki Ljubimac u nizu ispituje i broji pom oću rezervi-
sane reči instanceof.
Rezervisana reč instanceof im a prilično blago ograničenje: om ogućuje poređenja
sam o sa im enovanim tipom , a ne sa objektom tipa Class. Gledajući gornji prim er, možda
ste pomislili kako je dosadno pisati sve te izraze sa instanceof, i u pravu ste. M eđutim ,
nem a načina za pam etno autom atizovanje poređenja pom oću instanceof tako što će se
napraviti niz objekata tipa Class i porediti s njim a (ne očajavajte, ipak postoji alternativ-
no rešenje). To nije veliko ograničenje kao što m ožda mislite, pošto vam projekat i nije
baš najbolji ako pišete m nogo izraza instanceof.

Korišćenje literala klase


Ako prepravim o prim er PravljenjeLjubimaca.java tako da koristi literale klase, dobiće-
m o m nogo čitljiviji kod:

//: podaciotipu/1jubim ci/RucnoPravljenjeLjubim aca.java


// Korišćenje 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 nasumično 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 ično generisani), pa nam je neophodna Lista sviTipovi. Lista tipovi je
deo liste sviTipovi —napravljene m etodom List.subList( ); ona obuhvata tačne tipove
Ijubimaca, pa se upotrebljava za nasum ično pravljenje Ljubimaca.
Ovoga puta lista tipovi ne m ora da se pravi u bloku try, pošto se proverava tokom pre-
vođenja i stoga neće 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 ožem 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().
Pošto 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.

Dinamički instanceof
M etoda Class.islnstance nudi način za dinam ičko ispitivanje tipa objekta. Tako se sve do-
sadne naredbe instanceof m ogu ukloniti iz prim era PrebrojavanjeLjubimaca:

//: podaciotipu/PrebrojavanjeLjubim aca3.java


// K o rišćen 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 različiti 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 slučaju, nulu), i popunjava Mapu ključevima uzetim
iz liste sviTipovi i vrednošću nula. Da Mapu nism o popunili unapred, prebrojali bismo
sam o nasum ično generisane tipove, ali ne i osnovne tipove kao što su Ljubimac i Macka.
Vidite da zbog korišćenja m etode islnstance() nisu potrebni izrazi instanceof. Osim
toga, to znači i da nove tipove kućnih ljubim aca m ožete da dodajete jednostavnim me-
njanjem niza tipovi; ostatak program a ne m ora da se m enja (što nije bio slučaj pri koriš-
ćenju izraza instanceof).
M etodu to S trin g () preklopili sm o da bi davala čitljiviji izlaz koji i dalje odgovara
tipičnom izlazu koji se dobija ispisivanjem M ape.

Rekurzivno brojanje
Mapa u PrebrojavanjeLjubimaca3.BrojacLjubimaca bila je unapred popunjena svim
različitim klasama Ljubimac. Umesto da m apu popunjavam o unapred, m ožem o upotre-
biti m etodu Class.isAssignableFrom ( ) i napraviti alatku opšte 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 + " netačan 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 izvršavanja da li objekat koji ste prosledili
zaista pripada hijerarhiji o kojoj je reč. M etoda countC lass( ) najpre broji tačan 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 tačni tipovi.


Vežba 11: (2) Biblioteci podaciotipu.Ijubim ci dodajte klasu M orskoPrase i prepravite
sve prim ere u ovom poglavlju tako da se prilagode novoj klasi.
Vežba 12: (3) U potrebite BrojacTipova s klasom GeneratorKafe.java iz poglavlja Gene-
rički tipovi.
Vežba 13: (3) U potrebite BrojacTipova iz prim era RegisteredFactories.java u ovom
poglavlju.

Registrovane proizvodne metode


Ako generišete objekte hijerarhije Ljubimci, m orate se setiti da sve buduće tipove te hije-
rarhije dodate i u klasu rucnoPravljenjeLjubimaca.java. Ukoliko često pravite nove kla-
se, to m ože predstavljati problem .
M ožda ste pomislili da potklasam a treba dodati statični inicijalizator koji upisuje klasu
u neku listu. Nažalost, statični inicijalizatori se pozivaju prilikom učitavanja klase, pa se ja-
vlja problem kokoške i jajeta: kada klasa nije u listi poznatih tipova, rucnoPravljenjeLjubi-
maca ne može da pravi objekte te klase, a zbog toga klasa neće ni biti učitana ni stavljena
na Iistu.
U suštini, takvu listu m orate sami da napravite, i to ručno (sem ukoliko napišete 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 apravićem o još jednu izm enu - prebacićem o pravljenje objekta u klase, za šta ćemo
u potrebiti projektni obrazac Factory Method (Proizvodna m etoda). Proizvodna m etoda
pravi objekat odgovarajućeg tipa, a može 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

Generički p aram etar T znači da m etoda create( ) m ože da vraća različit tip rezultata za
razne realizacije interfejsa Proizvodjac.
U ovom prim eru, osnovna klasa Deo sadrži 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 generičkog 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 sadrže un utrašnju 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 grešku „generic array creation“ (pravljenje generičkog niza, što će biti
objašnjeno u poglavlju Generički tipovi). Umesto nje, koristio sam m etodu a d d ( ). Meto-
da createR andom ( ) nasum ično bira objekat iz liste proizvodjaciDelova i poziva njegovu
m etodu create( ) da napravi nov Deo.
Vežba 14: (4) I konstruktor je svojevrsna proizvodna m etoda. Prepravite program Regi-
strovaniProizvodjaci.java tako da se um esto eksplicitnog navođenja im ena proizvođača,
objekat klase smešta u listu, i svaki se objekat pravi m etodom new lnstance( ).
Vežba 15: (4) Realizujte novu kiasu PravIjenjeLjubimaca koristeći 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.
Vežba 16: (4) Izmenite hijerarhiju Kafa u poglavlju Generički tipovi tako da se koriste Re-
gistrovani Proizvodjaci.

Poređenje instanceof sa ekvivalencijama klase


Kada tražite inform acije o tipu, postoji važna razlika izm eđu 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 između 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 Izveđena 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 očekivano, instanceof i islnstance() daju istovetne rezultate, isto
kao i equals() i ==. M edutim , sami testovi dovođe do različitih zaključaka. 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 oću operatora = =, tu nasteđivanje
ne igra nikakvu ulogu: objekti su ili istovetnog tipa ili nisu.
Poglavlje 14: Podaci o tipu 461

Refleksija: informacije o klasi u vreme izvršavanja


Ako ne znate kog je tipa objekat, saznaćete iz prepoznavanja tipa tokom izvršavanja. Me-
đu tim , postoji jedno ograničenje: da biste tip mogli da otkrijete tokom izvršavanja i da bi-
ste uradili nešto korisno s tom inform acijom , on m ora biti poznat u vreme prevođenja.
D rugim rečima, prevodilac m ora da zna za sve klase s kojima radite kako bi prepoznao tip
tokom izvršavanja.
Na prvi pogled to ne deluje kao veiiko ograničenje, ali pretpostavim o da ste dobili re-
ferencu na objekat koji ne pripada prostoru vašeg program a. Zapravo, klasa objekta čak
nije ni do stu pn a program u tokom prevođenja. Na prim er, zam islim o da ste dobili gom ilu
bajtova iz datoteke s diska ili ste ih preuzeli iz m reže, a rečeno vam je da oni predstavljaju
klasu. Pošto prevodilac ne m ože da zna za tu klasu dok prevodi kod, kako uopšte možete
da je koristite?
U tradicionalnom program erskom okruženju ovo izgleda kao malo verovatno. M eđu-
tim , kako širim o vidike program iranja, srećemo važne slučajeve u kojima se to dešava. Prvi
slučaj je program iranje kom ponenata, gde se projekti prave u okruženju za brzo razvijanje
aplikacija (engl. Rapid Application Development, RAD), pom oću alatke za pravljenje apli-
kacija u integrisanom razvojnom okruženju (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 eštanja po obrascu ikonica koje prcilstavljaju kom ponente. Te
k om ponente se zatim konfigurišu podešavanjem 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 ože prikazivati svoje deiove i da dozvoljava čitanje i
postavljanje svojih vrednosti. Osim toga, kom ponente koje obrađuju grafičke događaje
m oraju da prikazuju inform acije o odgovarajućim m etodam a : ;ko bi razvojno okruženje
pom oglo program eru u redefinisanju m etoda za obradu dogudaja. Refleksija obezbeđuje
m ehanizam za otkrivanje postojećih 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 oću tehnologije zrna Jave (engl.
Java Beans) koja je opisana u poglavlju Grafička korisnička okruženja.
Još jedan značajan m otiv za otkrivanje inform acija o klasi u vreme izvršavanja jeste
om ogućavanje pravljenja i pokretanja objekata na udaljenim )_latform am a u mreži. To se
zove daljinsko pozivanje metoda (engl. Remote M ethod Invocation, RMI) a om ogućuje Ja-
vinom p ro gram u da koristi objekte koji su raštrkani na raznim računarim a. Raštrkanost
objekata je opravdana: na prim er, m ožda izvršavate zadatak s m nogo izračunavanja, pa
želite da ga podelite i pošaljete delove besposlenim računarim a kako biste ubrzali izvrša-
vanje. Ponekad ćete hteti da određenom računaru pošaljete kod koji obrađuje određene
vrste zadataka (npr. „poslovna pravila" u višeslojnoj kljent/sei ver arhitekturi), pa taj ra-
čunar postaje zajednička ostava koja opisuje akcije i može se lako prilagoditi tako da utiče
na sve u sistem u. (To je zanimljiv napredak, pošto je svrha računara isključivo da olakšava
prom ene softvera!) Uzgred, distribuirana obrada podržava i specijalizovan hardver koji
m ožda odgovara određenom zadatku, npr. inverziji matrice, ali nije pogodan ili je pre-
skup za program iranje opšte nam ene.
462 Misliti na Javi

Klasa Class, opisana ranije u poglavlju, podržava 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 ašina tokom izvrša-
vanja, da bi predstavila odgovarajućeg člana nepoznate klase. Zatim m ožete 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 ožete da pozovete m etode getFields(), getMet-
hods(), getConstructors() itd. da biste dobili nizove objekata koji predstavljaju polja,
m etode i konstruktore. (Više o ovom e naći ćete u dokum entaciji o klasi Class u Javinom
razvojnom okruženju, JDK.) Znači, inform acije o klasi an onim nih objekata m ogu se u
p otpunosti otkriti tokom izvršavanja, a ništa ne m ora da se zna tokom prevođenja.
Važno je shvatiti da refleksija nije ništa posebno. Kada koristite refleksiju za rad sa
objektim a nepoznatog tipa, Javina virtuelna m ašina će proveriti da li neki objekat pripada
određenoj klasi (kao p ri običnom p ostu p ku prepoznavanja tipa u vreme izvršavanja), ali
zatim , pre nego što uradi bilo šta drugo, m ora da učita objekat tipa Class. Stoga datoteka
.class za taj tip i dalje m ora da bude d o stu pna Javinoj virtuelnoj m ašini, bilo na lokalnom
sistem u ili u mreži. Prem a tom e, prava razlika izm eđu prepoznavanja tipa u vreme izvr-
šavanja i refleksije leži u tom e što u prvom načinu prevodilac otvara i ispituje datoteku
.class tokom prevođenja. D rugim rečima, m ožete da pozovete sve m etode objekta na
uobičajen način. Pri refleksiji, datoteka .class nije dostupna tokom prevođenja; nju otva-
ra i ispituje izvršno okruženje.

Čitanje metoda klase


Retko će biti potrebno da direktno koristite refleksivne alatke; one postoje u jeziku da bi
podržavale pravljenje dinam ičnijeg koda. Refleksija je stavljena u jezik da bi pođržala
druge Javine m ogućnosti kao što su serijalizacija objekata i zrna Jave (obrađena u nastav-
ku knjige). M eđutim , postoje prilike kada je m ogućnost dinam ičkog 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
pronalaženje je zam orno i oduzim a m nogo vrem ena.1Srećom, retleksija obezbeđuje način
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 vraćaju 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 eđutim , za dobijanje
znakovnog niza s potpunim potpisom m etode možete da koristite sam o m etodu to-
StringO, kao što je ovde učinjeno. O statak koda služi samo za izdvajanje inform acija s ko-
m andne linije, za utvrđivanje da li određeni potpis odgovara ciljnom znakovnom nizu
(korišćenjem funkcije indexOf()) i za odbacivanje kvalifikatora im ena p om oću regular-
nih izraza (koji su predstavljeni u poglavlju Znakovni nizovi).
Pošto rezultat koji daje Class.forName() ne m ože da bude poznat u vrem e prevođenja,
sve inform acije o potpisu m etode izdvajaju se tokom izvršavanja. Ako potražite objašnje-
nje refleksije u dokum entaciji na Webu, videćete da im a dovoljno podrške za stvarno po-
dešavanje i pozivanje m etode za objekat koji je p o tp u n o nepoznat tokom prevođenja
(prim era za ovo biće u nastavku knjige). lako ovo m ožda nikada nećete 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 sadrži 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 više neće 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 može da uštedi vrem e prilikom program iranja, kada ne m ožete 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, može da radi nešto sa objektiina
tipa Color.
Poglavlje Grafićka koristiička okruženja sadrži grafičku verziju ovog program a (prila-
gođenu za izdvajanje inform acija za Swing kom ponente), pa m ožete da je izvršavate dok
pišete kod da biste brzo pronašli ono što vam treba.
Vežba 17: (2) Izm enite regularan izraz u program u PokaziMetode.java tako da se
uklanjaju i rezervisane reči native i final. Uputstvo: upotrebite o perator I (OR).
Vežba 18: (1) Izm enite klasu PokaziMetode tako da ne bude javna i dokažite da se napra-
vljen podrazum evani konstruktor više ne pojavljuje u rezultatu program a.
Poglavlje 14: Podaci o tipu 465

Vežba 19: (4) U program u Ispitivanjelgracaka.java, u potrebite refleksiju za pravljenje


objekta tipa Igracka pom oću nepodrazum evanog konstruktora.
Vežba 20: (5) U dokum entaciji JDK na adresi http://java.sun.com potražite interfejs za ja-
va.lang.Class. Napišite 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 oću neke klase iz standardne biblioteke i neke klase koju ste sam i napravili.

Dinamički posrednici
Projektni obrazac Proxy (Posrednik) jedan je od osnovnih projektnih obrazaca. To je ob-
jekat koji umećete um esto ,,pravog“ objekta da biste obavili neke dopunske ili različite
operacije — m eđu njim a je obično i kom unikacija sa određenim „pravim " objektom .
S truk turu projektnog obrasca Proxy pokazaćem 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
* ///:-

Pošto p o tro sac( ) prim a jedan Interfejs, on ne m ože znati da li je dobio PraviObjekat
ili objekat tipa JednostavanPosrednik, jer oba realizuju Interfejs. Ali JednostavanPo-
srednik, koji je um etnut izm eđu klijenta i objekta PraviObjekat obavlja operacije i zatim
poziva identičnu m etodu za PraviObjekat.
Projektni obrazac Proxy je podesan kad god neke dodatne operacije treba izdvojiti od
„pravog objekta" i naročito kada hoćete da olakšate prelaz od nekorišćenja tih dodatnih
operacija na korišćenje i obrnuto (svrha projektnog obrasca jeste kapsuliranje prom ena
- da biste opravdali upotrebu obrasca, nešto m orate da menjate). Na prim er, kako biste
pratili pozive m etoda u obiektu PraviObjekat ili merili režijske troškove tih poziva? Tak-
vom kodu nije m esto u gotovoj aplikaciji, pa Proxy om ogućuje da ga lako dodate i uklonite.
Javin dinamički posrednik (engl. dynamicproxy) podiže ideju upotrebe posrednika na
viši nivo, pošto objekat posrednika pravi dinam ički i isto tako obrađuje pozive posredo-
vanih m etoda. Svipozivi dinam ičkogposrednika bivaju preusm ereni na jedini blokza po-
zive (engl. invocation handler) koji prepoznaje poziv i odlučuje šta s njim da radi. Preradio
sam ProstPrim erZaPosrednika.java tako da posređnik bude dinamički:

//: 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 ičkog posrednika pravite pozivom statične m etode Proxy.newProxy-Instance( )


koja zahteva učitavač klase (po pravilu, možete joj dati učitavač klase nekog več učitanog
objekta), listu interfejsa (ne klasa niti apstraktnih klasa) koje posrednik treba da realizuje
i realizaciju interfejsa InvocationHandler. Dinam ički posrednik preusm erava sve pozive
bloku za pozive, pa konstruktor bloka za pozive obično dobija referencu na ,,pravi“ obje-
kat, da bi mogao da prosleđuje zahteve nakon što obavi svoj posrednički posao.
M etoda in v o k e () dobija objekat posrednika, za slučaj da hoćete da saznate odakle je
poziv došao - što je retko potrebno. M eđutim , budite oprezni kada iz m etode invoke( ) po-
zivate m etode posrednika, pošto se pozivi kroz interfejs preusm eravaju kroz posrednik.
Po pravilu, obavićete posredovanu operaciju i zatim upotrebiti M ethod.invoke( ) za
prosleđivanje zahteva objektu koji ima posrednika, prosleđujući m u potreb n e argu-
mente. M ožda će vam to isprva izgledati ograničavajuće —činiće se da m ožete obavljati
sam o generičke operacije. M eđutim, pozive određenih m etoda možete izdvojiti filtrom,
dok ćete druge m etode sam o proslediti:

//: podaciotipu/IzborM etoda.java


// Traženje određenih metoda u dinamičkom 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 možete tražiti i druge delove potpisa
m etoda, pa čak i vrednosti određenih argum enata.
D inam ički posrednik nije alatka koju ćete upotrebljavati svakog dana, ali neke vrste
problem a njom e m ožete veoma lepo da rešite. O projektnom obrascu Proxy i drugim pro-
jektnim obrascim a više ć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).
Vežba 21: (3) Prepravite program ProstPrim erZaPosrednik.java tako da m eri vrem ena
poziva m etoda.
Vežba 22: (3) Prepravite program ProstDinam ickiPosrednik.java tako da m eri vrem ena
poziva m etoda.
Vežba23: (3) U nutar m etode invoke( ) u program u ProstDinamickiPosrednik.java, po-
kušajte da ispišete argum ent posrednik i objasnite šta se dešava.
Projekat:2 Napišite sistem u kojem dinam ički posrednici realizuju transakcije, gde po-
srednik obavlja potvrđivanje ako je posredovani poziv bio uspešan (nije generisao izuzet-
ke), o dno sno poništavanje ako je poziv zakazao. Potvrđivanje i poništavanje se obavljaju
na spoljnoj tekstualnoj datoteci koja je izvan kontrole Javinih izuzetaka. M oraćete da pa-
zite na atomizovanost (usitnjenost) operacija.

Null objekti
Kada ugrađenu null referencu upotrebljavate da biste naznačili kako određeni 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 ponašanje, sem što proizvodi NullPointerException ako išta pokuša-
te s njom da uradite. Katkada je korisno uvesti Null objekat* koji prim a poruke za objekat
koji ,,zam enjuje“, ali vraća 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-
ravajući reference na n u ll (i na čitanje rezultujućeg koda).
Iako bi bilo zabavno zamišljati 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 ože razborito pretpostaviti da null referenca neće
naići, dok im a i slučajeva kada je prihvatljivo otkrivanje grešaka preko izuzetka NullPo-
interException. Izgleda da su NuII objekti najkorisniji kada su „bliže 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 odgovarajuća provera. Umesto toga, m ožem 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 ršn 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 način ispitivanja jednakosti s null. Najjednostavniji način 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 ogućilo da in stan c eo f otkrije Null objekat, i što je još važnije, ne biste
m orali da dodajete m etodu is N u ll() svim svojim klasam a (što bi, na kraju krajeva, samo
predstavljalo drugačiji način prepoznavanja inform acija tokom izvršavanja - pa zašto
onda ne bism o koristili ugrađeni 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 statična finalna instanca. To funkcioniše zato što je Osoba nepromenljiva
—m ožete postavljati samo vrednosti u kon stru kto ru , i zatini ih čitati, ali ih ne možete
m enjati (pošto su objekti tipa String po svojoj prirodi neprom enljivi). Ukoliko hoćete đa
prom enite objekat tipa NuIlOsoba, jedino možete da ga zam enite novim objektom tipa
Osoba. Imajte u vidu da pom oću rezervisane reči instanceof i dalje možete otkrivati ge-
nerički objekat Null ili specifičniji objekat tipa NulIOsoba, ali singltonskim pristupom
m ožete se ograničiti na korišćenje m etode e q u a ls( ) ili čak operatora = = za poređenje 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 ožete 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, pošto postojanje objekta


tipa Osoba.NULL im plicira postojanje praznog objekta tipa RadnoMesto. Moguče je da
se kasnije m ora eksplicitno dodati Null objekat za RadnoMesto, ali YAGNI4 ( You Aren’t
Going to Need It, Neće biti potrebno) uči da u prvoj verziji program a pokušate s „najjed-
nostavnijim što bi moglo da fu nkđon iše“ i čekate dok vas neki aspekt program a ne natera
da dodate nešto novo; to je bolje nego da o dm ah pretpostavljate da će to biti neophodno.
Kada budete popunjavali radna mesta, klasa Osoblje će moći da traži 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 rađi.“


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 ", "Tehnički d ir e k to r " ,
"D ire k to r m arketinga", "D ire k to r proizvo dn je",
"Vođa 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 tehničkih 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("Dženet", “ 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: Tehnički d ire k to r
NullOsoba, RadnoMesto: D ire k to r marketinga NullOsoba, RadnoMesto: D irekto r
proizvodnje NullOsoba, RadnoMesto: Vođa projekta Osoba: Dženet 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 ičk ih tekstova NullOsoba]
* ///:-
Poglav[je 14: Podaci o tipu 473

Vodite računa 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 slučaju - 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 ožete u potrebiti Dyna-
micProxy za autom atsko pravljenje Null objekata. Pretpostavim o da im am o interfejs Ro-
bot koji definiše ime, m odel i List<Operacija> koji opisuju šta Robot um e da radi.
O peracija sadrži 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 ugnežđena klasa za testiranje.
Sada m ožem 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 + " može 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 + " može 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 + " može 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 može da č i s t i sneg
SamoRadi č i s t i sneg
SamoRadi može da č i s t i led
SamoRadi č i s t i led
SamoRadi može da p o č is ti krov
SamoRadi p o č is ti krov
* ///:-

Pretpostavljamo da će biti m nogo različitih tipova Robota i hteli bismo da svaki Null
objekat radi nešto posebno za svaki tip Robota - u ovom slučaju, sadrži podatke o tačnom
tipu Robota koji Null objekat zamenjuje. Te podatke će hvatati dinamički posrednik:
Poglavjje ) Podaci o tipu 475

//: p o daciotipu /N ullR obot.java


// P r a v lje n je Null objekta pomoću dinamičkog 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 može da č i s t i sneg
Snezana č i s t i sneg
Snezana može da č i s t i led
Snezana č i s t i led
Snezana može 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( ) prosleđujući joj tip Robota za koji hoćete posrednika. Posrednik ispu-
njava zahteve interfejsa Robot i Null i daje specifično im e tipa za koji posreduje.

Lažni objekti i vezivne funkcije


Lažni objekat (engl. Mock Object) i vezivna funkcija (engl. Stub) logičke su varijacije Null
objekta. Kao Null objekat, oni su zam ene za pravi objekat koji će biti upotrebljen u završe-
nom program u. M eđutim , i lažni objekat i vezivna funkcija glume da su živi objekti koji is-
poručuju prave podatke, um esto što su inteligentne zam ene za null, kao Null objekat.
Lažni objekat i vezivna funkcija razlikuju se u stepenu. Lažni objekti su uglavnom laki,
sami sebe testiraju, i obično se pravi m nogo njih za razne situacije tokom testiranja. Veziv-
ne funkcije vraćaju samo začetke podataka, obično su teški objekti i često se više puta upo-
trebljavaju u raznim testiranjima. Vezivne funkcije se m ogu konfigurisati tako da se izmene
u zavisnosti od načina na koji su pozvane. Dakle, vezivna funkcija je sofisticirani objekat
koji radi svašta, dok se za razne poslove pravi m noštvo različitih malih lažnih objekata.
Vežba 24: (4) D odajte Null objekte u program RegistrovaniProizvodjaci.java.

Interfejsi i podaci o tipu


Važna svrha rezervisane reči interface jeste da program eru om ogući da izoluje kom po-
nente i tako sm anji njihov m eđusobni uticaj. To se postiže pisanjem u interfejse, ali što se
tiče podataka o tipu, interfejse je m oguće zaobići - oni nisu savršeno jem stvo razdvajanja
realizacije klase od načina pristupa klasi. Za početak, 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, videćete kako se ipak može 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 ilaženje 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 grešku u prevođenju
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 izvršavanja 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 tešnje povežu s vašim 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 ožete reći da su p ro g ra m e ri sam i krivi ako su o d lučili da u p o tre b e klasu u m esto in -
terfejsa. To je v ero v atn o ta č n o u većini slučajeva, ali ako vam ,,verovatno“ nije dovoljno,
m o žete u p o tre b iti strože k o n tro le.
N ajlakše rešenje 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("zaštićena C .v ()“) ; }
private void w() { print("privatna C . w ( ) “ ); }
}

public class SkrivenaC {


public static A napraviA() { return new C(); }
} ///:-

N a jču 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 rešavali 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 ( ) vraćali C, zanim ljivo je da izvan pak eta i dalje n e biste
m ogli da koristite ništa osim A, p o što im e klase C izvan p ak eta n e m ožete n i d a u p otreb ite.
A ko sada p o k u šate d a svedete n an iže n a C, to n ećete 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
// Zaobilaženje 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 ) ;
// Greška tokom prevođenja: cannot find symbol 'C':
/* if(a instanceof C) {
C c = (C)a;
c.g();
) */
// Pazi sad! Refleksija ipak omogućava 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()
zaštičena 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 piše u definiciji m eto d e pozivSkriveneM etode( ).
Poglavlje 14: Podaci o tipu 479

M ožda m islite da ćete to sprečiti 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 vaših (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/UnutrašnjaRealizacija.java
// Privatne unutrašnje 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("zaštićena C.v()"); }
private void w() { print("privatna C.w()"); }
I
public static A napraviA() { return new C(); }

public class UnutrašnjaRealizacija {


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()
zaštičena C.v()
privatna C.w()
* ///:-

T im e se n išta 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 unutrašnje 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("zaštićena 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()
zaštićena C.v()
privatna C.w()
Poglavlje 14: Podaci o tipu 481

Kao da ne postoji n ač in da se spreči 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 važi 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. Izvršni sistem se neće žaliti n a p o -


kušaje takv ih izm ena, ali ih neće ni sprovesti.
482 Misliti na Javi

Po pravilu, sva ta kršenja 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 označili 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 teško m ože 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 ože o m o g u ćiti da rešite o d ređ en e vrste p ro b lem a koji bi
inače bili teški ili nem ogući, a p red n o sti refleksije su, u o p šte n o govoreći, neosporne.
Vežba 25: (2) N apravite klasu koja sadrži p riv a tn e m eto d e , zaštićene m eto d e i m eto d e
s p a k etn im p ristu p o m . N ap išite 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.

Sažetak
P rep o zn av an je tip a to k o m izvršavanja 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 ,
teško 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 stići o d re đ iv a n je m tip a u v re m e izvršavanja, 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 ržav 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 izvršavanja 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 ačin , 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 sadrži p o tre b n u m eto d u . Ako osnov-
na klasa p o tiče iz biblioteke ili je k o n tro liše n eko d ru g i, rešenje p ro b le m a leži u
p rep o zn av an ju tipa to k o m izvršavanja, 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 neće b iti u p ro p ašćen 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 izvršavanja da b iste o tk rili o d re đ e n i tip.
Z bog sm eštan 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-
finišu 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 zičke 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 ačk 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 aračk i i žičani in s tru m e n ti im aju pisak. P rep o zn av an je tip a u v rem e iz-
vršavanja o bezb eđ u je m n o g o p o g o d n ije rešenje u ovom slučaju zato što m e to d u m ožete
staviti u o đ re đ e n u klasu (u o v o m slučaju, u klasu d u v ačk 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 rešenje 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 ećete 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 ogrešno zaključiti 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 izvršavanja.
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 izvršavanja p o n e k a d se rešavaju p ro -


b le m i sa efikasnošću. 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 zahvaljujući p re-
p o z n a v a n ju u v rem e izvršavanja i d a napišete k o d za taj p o se b a n slučaj da b iste povećali
efikasnost. Ipak, n e m o jte da srljate u p ro g ra m ira n je rad i povećanja 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 ešto što se m ože p ro v e riti tek u v rem e
izvršavanja 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-
tičke p ro v ere tip o v a kao p o g rešan sm er. N eki p re te ru ju , p a kažu k ako je u v o đ en je m o -
g u ć n o sti pojave izuzetk a u v re m e izvršavanja jasan pokazatelj da takav k o d treb a
izbegavati. S m a tra m d a je to osećan je b ezb ed n o sti ilu z o rn o - u v rem e izvršavanja uvek se
m o že d esiti n ešto što će izazvati izuzetak, čak i u p ro g ra m u koji ne sad rži n i blokove tr y
n iti specifikacije izuzetaka. M islim d a d o sled n i m o d el prijavljivanja grešaka om ogućujed a
p iše 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 statički p r o v e r iti... k ad a je to m oguće. Ali sm a tra m da je d in a m ič k i k o d jed -
n a o d v ažn ih m o g u ć n o sti koje odvajaju Javu o đ jezika kao što je C + + .
V ežba 26: (3) R ealizujte m e to d u p ro c istiP is a k () kako je o p isan o u sažetku.
R ešen ja o d a b r a n ih v ežb 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.
Generički tipovi
O BIČN E KLASE I METODE RADE S POTPUN O ODREĐENIM TIPOVIMA: BILO PROSTIM, BILO
tip o v im a klasa. A ko pišete k o d k o ji se m o ž e u p o tre b iti s više tip o v a, ta k ru to s t u m e p re-
više d a o g ran ičav 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 vaša m e to d a p o staje n ešto o p štija i m o že se u p o tre b lja v ati na više m esta. Isto
važi 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 veću 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 ećin o m d o b ija au to m atsk i.
P on ek ad o g ran ičen o st n a je d n u h ije ra rh iju previše sputava. U koliko je a rg u m e n t m e-
to d e interfejs a n e klasa, o g ran ičen ja se ub lažav 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 vašoj klasi ili m eto d i. Tako interfejsi o m o g u ću ju
p rem ošćavanje 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 previše o g ran ičav 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 eričk ih tip o v a - o n i pred stav ljaju je d n o o d značajnijih
p o b o ljšan 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 ičn o k o n te jn e ra ) koje se lako u p o tre -
bljavaju s više tip o va. T erm in ,,generički“ znači „koji o d g o v ara velikim g ru p a m a klasa ili
važi za n jih “. G eneričk 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 najveću m o g u ću izražajn o st k ad a p išu klase ili m eto d e, ta k o što se ublažavaju ogra-
ničenja 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 generički tip o v i n isu to lik o m o ć n i - štaviše, m ogli biste se zap itati da Ii reč „gene-
ričk 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 generički 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 liči na pob o ljšan 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 ećete d a s Javinim g en eričk im tip o v im a ne m o žete da
u ra d ite baš sve što b iste o d njih očekivali. K orišćenje tu đ ih gen eričk ih tipova je prilično
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 šaću da varn
o b jasn im kako je došlo d o takve realizacije g en eričk ih tip o v a u Javi.

1 Angelika L anger piše Java Generics FAQ (O dg ov ori na najčešća pitanja o Javinim generičkiin 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: Generički tipovi 485

N e kažem d a su Javini g en eričk i tip o v i b esk o risn i. U m n o g o slučajeva, 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 eričk ih tip o v a, m o g li b iste d a se razočarate. U o v o m poglavlju istražićem o i
jake i slabe stra n e Javinih gen eričk ih tipo va, d a b iste d elo tv o rn ije m ogli d a ih koristite.

Poređenje sa C++-om
P ro je k ta n ti Jave kažu 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 eričk i tip o v i z ah te v aju više 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
generičke 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 ažn o - o g ra n ič en ja p rilik o m u p o tre b e Javinih g en eričk 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 ože (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 značajan 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 š više 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 eričk 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 generički tipovi


Jedna o d n ajvažn ijih p o č e tn ih m otiv acija za u v o đ en je gen eričk ih tip ov a bila je pravljenje
kotitejnerskih klasa, koje ste u p o zn ali u poglavlju Č uvanje objekata (a naučićete jo š u
poglavlju D etaljno razm atranjc kontejnera). K o n tejn er je m esto za skladištenje o b jek ata
d o k ra d ite s njim a. Isto važi i za nizove, ali k o n te jn e ri su fleksibilniji i im aju d rug ačija o be-
ležja 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 najčešće 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 tačan 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 skladištenje


ičega d ru g o g a. Bilo bi bolie da ne m o ra m o da p išem 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 ože da p rim i b ilo šta - a u ovom p rim e ru isto Skladiste2 čuva tri
različita tip a objekata.
Im a slučajeva kada hoćete da k o n tejn er sldadišti više tipova objekata, ali se u k o n tejn er
o b ičn o stavlja je d a n tip objekata. Jedna o d o sn o v n ih m otivacija za generičke tipove bilo je
zadavanje tipa objekta koji k o n tejner sadrži, i da tu specifikaciju pod rži 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 ože 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 veće o d
n ap išete 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"); // Greška
// h 3 . s e t ( l ) ; // Greška
}
} ///:-

K ada sada n aprav ite neko Skladiste3, istom sintak so m sa znakov im a m an je od i veće 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, pošto p rin cip zam ene
Poglavlje 15: Generički tipovi 487

fu n k cio n iše i u generičkim tip o v im a). A k a d a iz njega izvadite nešto, o n o je a u to m atsk i


isp ravno g tipa.
To je o sn o v n a ideja Javinih generičk 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 , generičke 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, generički 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.
Vežba 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.
Vežba 2: (1) N ap rav ite klasu skladista koje čuva tri objekta istog tipa, m e to d e za skla-
dišten je i vađenje 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 više 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 rešenje n a p ra v iti o bjekat koji sad rži više o b -
jek a ta koje h o ćete da v ratite. N arav n o , m o žete da pišete 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 eričk ih tipova p ro b le m m o žete rešiti je d n o m
zauvek i tak o sebi ušted eti tru d u b u d u će. Isto v rem en o , u vrem e p rev o đ en ja svi generički
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 ože 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 različitog 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 različitih d u žin a rešićem o
prav ljen jem različitih 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 sačuvati, 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 krše p rin c ip i bez-


b e d n o sti u o b ičajen 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 slučaju: 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 hoće, ali n e b i m o g li d a ih d o d e le n iče 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 raći 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 eđn 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 ačijim elem en tim a.
D uže n -to rk e se p rave n asleđ iv an jem . V idećete d a je d o d av an je većeg 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: Generički 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 efinišete 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"; // Greška u prevođenju: 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 erićk ih tip o v a lako m o ž e te p o stići 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 išete o d g o v araju ći izraz.
M ožete v id e ti kako o d re d b a final za jav n a p o lja sprečava 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 idećete kako da ih
p o jed n o stav ite generičkim m etodam a.
Vežba3: (1) N ap rav ite i isp ro b ajte g en eričk u Sestorku.
Vežba4: (3) U op štite p ro g ra m unutrasnjeklase/Sekvenca.java p o m o ć u generičkih tipova.

Klasa steka
P ogledajm o nešto što je m alo kom pliko vanije: klasični 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 eričk e klase (Stack<T>) s d ru g o m g eneričkom
klasom (LinkedList<T>). N a to m p rim e ru vid ite da je generički 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 skladištenje.

//: g e n e ri ck i/ U1 an ca ni Ste k.java


// Stek realizovan pomoću unutrašnje ulanćane 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: Generički 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 eričk 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 lančava s p re th o d n im o b jek to m tip a Cvor<T>. Kada
pozovete sk in isa( ), uvek vraćate 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 sledeći - s e m kada n aiđ ete na o zn a k u k raja, je r s e u to m slučaju 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 ežba 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 kažete da u n u tra š n ja klasa im a p ris tu p gen eričk 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( ). Pošto hoćete alatk u koja radi
sa svim o b jek tim a, k o ristite g eneričke 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
* ///:-

Vežba 6: (1 ) U p o tre b ite k lasu NasumicnaLista s dva tip a više n ego što je p o k az an o u m e-
to d i m a in ().

Generički interfejsi
G enerički 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 definiše 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 uslužn e klase:

//: n e t/ mi nd vi ew /u ti1/Generator.java
// Generički 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, generički 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 azaćem 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: Generički 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 {} ///:-

Sađa m o ž em o da realizu jem o G e n e ra to r< K a fa > koji p rav i različite tipove K afa ob je-
kata:

//: genericki/kafa/GeneratorKafe.java
// Generiše različite 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 izvršavanja, prijavi greške 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( ) vraća 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: Generički 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 eričk 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 tići 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 pišete 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 više način a. N a p rim er, a d a p tira n u k lasu m o žete d a
generišete n asleđivanjem :

//: 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

Vežba 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 nasleđi-


vanja.
Vežba 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 vašeg
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.

Generičke metode
D o sad sm o p a ram etrizo v ali cele klase. M ožete 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 ože, ali n e m o ra b iti gen eričk a - to ne zavisi o d g en e rič n o sti m eto d e.
G en eričn o st m eto d e znači da o n a m o že da se m en ja n ezav isn o o d klase. Po p rav ilu , ge-
neričke 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 rečim 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 eričk 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 statičn a, o n a n e m a p ristu p a g e n eričk im p a ra -
m e trim a tip a (svoje generičke) 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 generička.
G enerička m e to d a se definiše stavljanjem liste g en eričk 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 slučaju, 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: Generički tipovi 497

V odite ra č u n a o to m e d a p a ra m e tre tip a g eneričke 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 generičke 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 zaključi-
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 ičn e m eto d e; izgleda k ao da je f ( ) p rek lo p ljen a b esko načan 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 eneričke 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.
Vežba 9: (1 ) Izm en ite GenerickeMetode.java tak o da f ( ) p rim a tri arg u m e n ta različitih
p a ra m e triz o v a n ih tipova.
Vežba 10: (1) Izm en ite p re th o d n u v ežbu 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 .

Korišćenje zaključivanja o tipu argumenta


Jed n a o d p rim e d a b a n a ra ču n generičk 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 reči extends i zn ak a p ita n ja o b jasn ićem 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 generičkih
a rg u m e n a ta shvati šta d a stavi u d ru g u . N ažalost, o n to ne m o že, ali zaključivanje o tip u
a rg u m e n ta u generičkoj m e to d i m ože 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 najčešće u p o tre b lja-
vane realizacije razn ih k o n tejn era:

//: net/mindview/util/Nova.java
// Uslužne metode koje pravljenje generičkih kontejnera
// pojednostavljuju tako što same zaključuju 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 korišćenja ove klase - zak ljučivanje 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 eričk ih p a ra m e ta ra . To se m ože 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 zaključivanja o tip u arg u m e n ta , teško je reći 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 veća, 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 aključivanje o tip u arg u m e n ta fun k cio niše isključivo 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 nećep o k u šati da zaključi 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 spešno 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: Generički 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 ) ; // Neće biti prevedeno
}
} ///:-

Vežba 11: ( 1) Ispitajte klasu Nova.java p rav ljen jem so p stv en ih klasa. D o kažite d a Nova s
njim a rad i ispravno.

Eksplicitno zadavanje tipa


T ip generičke 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 iše u n u ta r znakova m an je o d i veće o d , iza tačk 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 tačke, a kad a ra d ite
sa sta tič n im m e to d a m a , isp red tačke 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 rešiti 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 korišćenja klase Nova koja sm an ju je k o ličin u pisan ja,
ali je d o d a tn a sintaksa p o tre b n a sam o kada ne pišete n are d b e dodeljivan ja.
V ežba 12: (1) P onovite p re th o d n u vežbu uz eksp licitn o zadavanje tipova.

Argumenti promenljive dužine i generičke metode


G eneričke 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.

Generička 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
// Uslužna metoda za korišćenje 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: Generički 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 ože 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.
Vežba 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 nećete izgubiti tip kon-
tejn e ra . M ožete li p re k lap a n jem u čin iti d a se List i LinkedList razlikuju?

Generator opšte 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 eneričku 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 generišete.
G enerička m e to d a n a p ra v i( ) o m o g u ću je d a kažete Elem entarniG enerator.napra-
vi(MojTip.cIass) u m esto nevv ElementarniGenerator<M ojTip>(M ojTip.cIass), što bi
bilo ružnije.
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 generička m eto d a sm an ju je količinu pisanja kada se pravi objekat
Generator. Javin generički 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 ožete u p o treb iti i za zaključivanje o tip u arg u m en ta u m eto d i napravi( ).
Vežba 14: ( 1) Izm en ite PrimerElem entarnogGeneratora.java tako da se Generator pravi
eksplicitno (tj. u m esto generičke 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 aključivanje 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 statičn o m m e to d o m :

//: net/mindview/util/N_torka.java
// Biblioteka n-torki u kojoj se upotrebljava
// zaključivanje o tipu argumenta.
package net.mindview.util;
Poglavlje 15: Generičkl 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 ićem 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)
(Vozilo®757aef, Amfibija@d9f9c3, zdravo, 47)
(Vozilo@la46e30, Amfibija@3e25a5, zdravo, 47, 11.1)
* ///:-

V odite ra č u n a o to m e da f ( ) vraća p aram etrizov an objekat tip a Dvojka, d o k f2 ( ) vraća


n ep aram etrizo v an objekat tipa Dvojka. U ov om slučaju 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 način; n a neki
način , o n a se „svodi naviše" n a n ep aram etrizo v an tip Dvojka. M eđ u tim , kada biste pokušali
da rezu ltat f2 ( ) uhvatite u p aram etrizo v an tip Dvojka, prevodilac b i d ao upozorenje.
Vežba 15: (1) Prov erite p re th o d n u tv rd n ju .
Vežba 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.

Uslužna metoda za Set


Kao jo š jed an p rim e r u p o tre b e g en eričkih m eto d a, ra z m o trić e m o m a te m a tičk 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 različitim 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: Generički 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 atičk e o p eracije sa sk u p o v im a: u n ija ( ) vraća
Set koji sadrži k o m b in aciju dva a rg u m e n ta, p resek ( ) vraća Set koji sad rži zajed n ičk 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( ) vraća 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, sledeći enum (n a b ro ja n i tip ) sad rži 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 statičn o uvezem o u sledeći 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 više 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: Generički 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 „Sažetku“ poglavlja Č uvanje objekata.
Vežba 17: (4) P roučite JDK d o k u m en ta ciju za EnumSet. V idećete 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 ožete 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 opšti
slučaj interfejsa Set (kao što je p rik azan o ) i specijalni slučaj 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 unutrašnje klase


G enerički 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 službenika 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 + " uslužuje 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: Generički tipovi 509

) /* Ispis:
Sluzbenik 3 uslužuje kl ijenta K1ijent 1
Sluzbenik 2 uslužuje kl ijenta K1 ijent 2
Sluzbenik 3 uslužuje kl ijenta K1 ijent 3
Sluzbenik 1 uslužuje kl ijenta K1 ijent 4
Sluzbenik 1 usl užuje kl ijenta K1 ijent 5
Sluzbenik 3 uslužuje kl ijenta K1 ijent 6
Sluzbenik 1 uslužuje kl ijenta K1 ijent 7
Sluzbenik 2 uslužuje kl ijenta K1ijent 8
S1uzbeni k 3 uslužuje kl i jenta K1 ijent 9
Sluzbenik 3 uslužuje kl ijenta K1ijent 10
Sluzbenik 2 uslužuje kl ijenta Klijent 11
Sluzbenik 4 uslužuje kl ijenta K1 ijent 12
Sluzbenik 2 uslužuje kl ijenta K1ijent 13
Sluzbenik 1 uslužuje kl ijenta K1 ijent 14
Sluzbenik 1 uslužuje 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 ožda v am neće 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 ( ).
Pošto su statičn 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 eneričk 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 izvršavanje.
Vežba 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 složenih modela


G en eričk 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 složenih 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 generičkih tipova da bi se dobili
// složeni generički 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 riličn 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 složenih m o d ela
p o m o ć u generičkih 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 slučaju, m o d el je p ro d av n ica s pasažim a, p o lica m a i pro izv o d im a:

//: genericki/Prodavnica.java
// Pravljenje složenog modela pomoću generičkih 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: Generički 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 teško.
Vežba 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 skladišta.
512 MislitinaJavi

Tajanstveno brisanje
K ako b u d e te sve tem eljnije u p o zn av ali generičke tipove, nailazićete na stvari koje v am
sp očetk a neće biti logične. N a p rim er, iako se m ože reći ArrayList.class, n e m o že se reći
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> različiti tipo vi. Razli-


čiti tip o v i se p o n aša ju različito, i ako p o k u šate, p riin e ra rad i, da stavite Integer u Array-
List<String>, d o bićete različito p o n a šan je (to neće 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 kaže d a su o b a isti tip.
Evo p rim e ra koji će vas z b u n iti jo š više:

//: 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: Generički tipovi 513

} /* Ispis:
[E]
[K, V]
[Q]
[POL OZ AJ, MOMENAT]
* ///:-

U JDK d o k u m en tac iji piše d a m e to d a CIass.getTypeParameters( ) „vraća niz o bjekata


tip a TypeVariable koji pred stav ljaju p ro m en ljiv e tip a dek larisan e u generičkoj 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 generičkom kodu uopšte nisu dostupne informacije o tipovima generičkih
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 iče n ja ge-
n eričk 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 eričk im tip o v im a.
Javini generički tip o vi realizuju se uz brisanje (engl. erasure). To zn ači sledeće: kad a
u p o tre b ite generički tip, b rišu se sve specifične in fo rm ac ije o tip u . U n u ta r gen eričk 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 izvršavanja 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 učenja Javinih g en eričk ih tipova, gotovo d a će n ajteže 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 idećete
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 sadrži 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 ože 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 rešk 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 očuvana.
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 generički tip o v i su drugačiji.
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 iše m o n a Javi, o n neće m oći d a se prevede:

//: ge nericki/Manipulacija.java
// {Compi1eTimeError} (Ne može da se prevede)

class Manipulator<T> {
private T obj;
public Manipulator(T x) { obj = x; }
// Greška: 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 oći da poziva f ( ) za o b j, Javin prevodilac zbog
brisan ja ne m ože 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 generičkoj klasi dajući joj granicu (engl. bound) koja prev o d io cu
k azuje da prihvata sam o tipove usaglašene 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. Sledeći p ro g ram će se usp ešn o prevesti, u pravo zbog granice:
Poglavlje 15: Generički 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 ažem o d a se p a ra m e ta r generičkog tip a briše do svojeprve granice (g ran ica m o ž e b iti
više, što ćete v ideti u n astav k u ) ili d a se briše 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 slučaju 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 enerički tip o v i ne
ra d e ništa. 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 eričk 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 došli do v ažn o g zaključka: generički 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 opštiji 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 različitim klasam a. Z ato su, u k o risn o m gene-
ričk 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 složeniji o d p ro ste z am e n e klasa.
M eđ u tim , to ne znači 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 vraća T, o n d a je generički 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 korišćenje generičk 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 složen.
G ran ice će m o d etaljn ije ra z m o triti u n astavku poglavlja.
V cžba 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 eričk u m e to d u čiji je a rg u m e n t tip a o g ran iče n in-
terfejso m i pok ažite da se m eto d e interfejsa m o g u pozivati iz te generičke m eto d e. U m e-
to di m a i n ( ), g eneričkoj 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 sprečili 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'eobeležje jezika. R adi se o k o m p ro m isu u realizaciji Javinih g eneričkih 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 navići n a njega i sh vatiti zašto je u čin jen.
D a su generički 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 jezičke i refleksivne o p eracije zasno van e na
tip o v im a. V idećete u n astav k u poglavlja d a b risan je sm a n ju je ,,op štost“ generičk 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 erički 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 erički tip ov i
p o stoje sam o to k o m statičke pro v ere tip o v a. N ak o n toga, svi generički 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 ričk 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 ičn 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 rišćenje g eneričk ih klijenata
s n eg en eričk 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 generički. U stv arn o sti, čak i da p ro -
g ra m e ri p išu sam o generički k o d , m o rali bi da vode ra č u n a o n eg en eričk 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 neće p rep rav iti svoj k o d tako
da p o sta n e generički, ili će to m o ž d a u ra d iti polako.
Z ato Javina realizacija g en eričk ih tip o v a m o ra da p o d ržav a n e sam o vertikalnu kom pa-
tibilnost - postojeći ko d i d ato tek e klasa m o ra ju o stati legalne i značiti isto što su značile
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 generičke 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 -
lučili su d a je b risan je je d in o ostvarivo rešenje. 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 eričko g k o da zaje dn o s generičkim .
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. Pošto 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 generički kod. M eđ u tim , svaki od
njih će im ati d ru g ačiju m o tiv aciju i o g ra n ičen 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 eričk 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 generički kod. Z ato do k az da o d ređ en a
bib lio tek a k o risti generički 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 odsečene o d p ro g ram era koji su odlučili da p red u na Javin generički
kod. T vrdi se d a su biblioteke deo jezika koji najviše u tiče 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, pokazaće vrem e.
Poglavlje 15: Generički 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 eričko g n a generički k o d i n a m e ra
da se gen erički k o d u ključi u jezik b ez ru še n ja p o sto ječih b ib liotek a. B risanje o m o g u ću je
p o sto jećem n eg en eričk 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 generički. Ta m otiv acija je p lem en ita,
zato što ne ruši o d je d n o m sav p o sto jeći kod.
C ena b risan ja je zn a tn a . G en e ričk 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 izvršavanja izričito u p u ć u ju n a tipove, k ao što su eksplicitne konverzije
tipova, o p eracije instanceof i izrazi new. P ošto se g u b e sve inform acije o tip u p a ra m e ta ra ,
kad a pišete generički k o d m o ra te se staln o p o d sećati 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 pišete ovakvo p arče 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 pišete ko d za tu klasu, m o rate sebi reći: ,,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 znače d a se generički 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 greška:
// unexpected type found : ? (pronađen neočekivan 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 nasleđuje k lasu GenerickaOsnovna b ez gen eričk 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 isključivanje 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-


riše u p o zo re n je , a ne p re cele klase. K ada isključujete u p o z o re n je , b o lje je d a se u sred sre-
d ite na što ,,uže“ p o d ru čje, d a ne biste p reširo k im isključivanjem u p o z o re n ja slučajno
sakrili neki p rav i p ro blem .
G reška k oju proizvodi Izvedena3 valjda znači 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 nešto više o d o b ičn o g objekta, i dob ili ste m n o g o više 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 ači da su ti jezici bolji o d Jave za v ećin 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 generičkog k o d a koji najviše z b u n ju je jeste činjenica da
m ožete 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: Generički tipovi 519

Iako je klasa vrsta n a p isa n a kao Class<T>, b risan je znači d a če o n a b iti usklad išten a
sam o kao Class, b ez p a ra m e tra . Z ato, k ad a s n jo m n ešto 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 ože d ati rezu ltat specifičnog 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 eričk 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 rugačije:

//: 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 izvršavanja u n u ta r
klase n em a nikakvog <T>, p a o n kao d a ne zn ači ništa. 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 slučaju p a ra m e ta r tip a zaista n e zn ači ništa? Šta bi se desilo d a n a sledeći
način 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 ništa 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 ože 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 ože 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 ože 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 ošto 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 izvršavanja važne su
granice. tačke 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 sledeći n e-
g enerički 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 iće-


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: Generički 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 bacićem o sada generičke 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( );
}
} ///= -

Više 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 išta, 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 eće prevodilac, p a je k o d
koji m o ra te d a n ap išete (i p ro č ita te) čistiji.
Pošto 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 eričk o m k o d u se sve što je važno
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 anjićete z b rk u o k o b risanja ako ne zaboravite d a se
,,na g ran icam a odigrava sve što je važno“.

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-
ričk 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 izvršavanja,
neće raditi:

//: genericki/Obrisana.java
// {CompileTimeError} (Neće biti prevedena)

public class Obrisana<T> {


private final int VELICINA = 100;
public static void f(0bject arg) {
if(arg instanceof T) {} // Greška
T var = new T(); // Greška
T[] niz = new T [ V E L I C I N A ] ; // Greška
T[] niz = (T)new O b j e c t [V E L I C I N A ] ;
// Upozorenje koje nije zaustavljeno
}
} ///:-

D ešava 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 znači 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: Generički 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 eričk o m a rg u m e n tu .


Vežba 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 grešci.

Pravljenje instanci tipova


P okušaj pravljenja nove generićke 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 revođenja):

/ / : 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 pokazivača:
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 rešenje 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. Pošto se greška ne hvata u v rem e p rev o đ en ja, ovaj
p ristu p se ne sviđa 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 -
izvodaća i o g ran ićite tip, tako da p rim a sam o klasu koja realizuje to g proizvodaća:

//: genericki/OgranicenjeProizvodjaca.java

interface ProizvodjacI<T> {
T n a p r a v i ();
}

class Nesto2<T> {
private T x;
Poglavlje 15: Generički 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 način a p ro -
sleđ u ju se p ro izv o d n i objekti; C la ss< T > je ig ro m slučaja 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 ođenja.
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 ( ) definiše 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
* ///:-

Vežba 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.
Vežba 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.
Vežba 24: (3) Izm en ite vežbu 21 tako da se p ro iz v o d n i ob jek ti d rže u Mapi u m esto u
Class<?>.

Nizovi generičkih tipova


Kao što ste videli u p ro g ra m u Obrisana.java, ne m o žete p rav iti nizove g en eričk ih tipova.
O p šte rešenje 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 eneričkih
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 ašan je niza, ali i p ro v eru tipova u v rem e p re v o đ en ja koju do n o si
generički kod.
K atkada će v am ipak zatreb ati niz g eneričkih 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 ožete 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: Generički tipovi 527

Prevodilac će to p rih v a titi bez ikakvog u p o zo ren ja . Ali p o što nećete 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 ošto svi
n izovi im a ju istu stru k tu r u (veličinu svakog e le m e n ta i ra sp o red elem en ata) bez o b zira na
tip koji sadrže, 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 neće m o ći da se izvršava, 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) {
// Prevešće se, ali pravi ClassCastException:
//! gia = (Generic<Integer>[])new O b j e c t [ V E L I C I N A ] ;
// U vreme izvršavanja, 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(); // Greška u vreme prevođenja
// Otkriva neslaganje tipova u vreme prevođenja:
/'/! 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 izvršavanja, to je sam o n iz tip a Object, i to stvara
p ro b le m e. Jedini n ačin da u sp ešn o n ap rav ite niz g en eričk 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 nešto teži p rim er. R azm o trićem o je d n o sta v a n g en eričk 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 reći 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 ( ) vraća 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 ićete
ClassCastException, i o p e t zato što je stv arn i tip u v rem e izvršavanja 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 slučaju, 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. Pošto 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 očekivali 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 izvršavanja 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-
vođilac neće prov erav ati i tim e sprečiti p o ten cijaln e greške. 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: Generički 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 drugačije, sa m o što je konverzija p rem ešte n a. Bez an o tacija
@SuppressWarnings, d o b ijaćete 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 tačan 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 izvršavanja. D ak-
le, n e m a n ačin a da se o b o ri tip p rip a d n o g in te rn o g niza koji m ože 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 izvršavanja zab o rav iti koji je tip niza i zato slu čajn o n a p rav iti
g rešku (iako se većina tak v ih grešaka, a m o žd a i sve, b rzo o tk rije u v rem e izvršavanja).
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 slučaju, 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
izvršavanja, tip niza je tača n tip T [ ] .
N ažalost, 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 naći 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 čiščenja 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 aći ćete m n o štv o takv ih eksp licitnih konverzija.
I šta se dešava 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, sećate se je d n e posledice
upo zo ren ja: kada shvatite da m ožete 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 ećih p ro g ra m e ra Jave SE5) ističe 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 kaže 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: Generički tipovi 531

sru ši postojeći 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 znači d a je to p rav i n ačin 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 ičen 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 eričk o m k o d u . Iako tim e d o b ijate m o g u ćn o st da n am ećete p ravila o
tip o v im a n a koje m ožete p rim e n iti svoj gen erički k od , p o ten cijaln o važniji efekat je d a
m o žete p ozivati m e to d e d efin isan e u vašim o g ra n ič e n im tip o v im a.
P ošto 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 generički 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 ičen je, Javin gen eričk 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 generičk ih g ran ica, ex-
te n d s im a sasvim d ru g ačije zn ačen je n ego o b ičn o . U n a re d n o m p rim e ru p rik azaćem 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 omogućuje 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 neće raditi -- prve moraju biti klase, a tek onda interfejsi:
// class Ob oj enaDimenzija<T extends ImaBoju & Đimenzija> {

// Više 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 nasleđivanju, možete imati samo jednu


// konkretnu klasu, ali više 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 ožda ste p rim e tili da O sn o v n o O G ra n ic a m a .ja v a sadrži p o n avljan ja koja se m og u iz-
beći nasledivanjem . Sada ćete v ideti kako svaki nivo nasleđivanja d o d a je svoja og raničenja:

//: 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: Generički 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 sadrži 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 usaglašen 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 nasleđuju , pa se n e m o ra ju p o n av ljati u svakoj klasi.
Evo p rim e ra s više slojeva:

//: genericki/EpskaBitka.java
// Primer granica u Javinom generičkom 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 generičkim 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 možete da uradite:
List<? extends SuperSluh> audioFili;
// Ali ovo ne možete:
// List<? extends SuperSluh & SuperNjuh> k e r o v i ;
}
} ///:-
V odite ra č u n a o to m e da se džokerski a rg u m e n ti (koje čem o sledeće 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 ežba 25: (2) N aprav ite dva interfejsa i klasu koja ih realizuje. N ap rav ite dve generičke
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 ičen 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 ičen d ru g im in terfejso m . N ap rav ite p rim e ra k klase koja realizu-
je oba interfejsa i pokažite da se o n a m ože u p o treb ljav ati sa oba interfejsa.
Poglavlje 15: Generički tipovi 535

Džokerski argumenti
Već ste videli neke jed n o sta v n e u p o treb e džokerskih argum enata -z n a k o v a p ita n ja u izra-
zim a generičkih a rg u m en a ta - u poglavlju Č uvanje objekata, a jo š više u poglavlju Podaci
o tipu. U ovom odeljk u ra z m o trić em o ih detaljnije.
Počećem 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 ožete 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 Narandža 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 izvršavanja tip je Jabuka[], ne Voce[] niti Narandža[]:
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 Narandže:
voce[0] = new Narandža(); // 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: Narandža
* ///:-

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 voća, 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 izvrša
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, pošto o n im a referencu n a Voce[] - zašto 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 Narandža? Stoga je
u v rem e prev ođ enja to dozvoljeno. M eđ u tim , u v rem e izvršavanja, m e h an izam nizova
zna da radi s nizom Jabuka[] i generiše izuzetak kada se stra n i tip stavi u taj niz.
„Svođenje naviše“ 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 aša kao d a sadrži d ru g e objekte, ali p o što m o žem o d a svedem o naviše, 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 sadrži. To je kao d a nizovi
zn aju šta sadrže, pa ne m ožete da ih p revarite zbog pro v era u v rem e p re v o đ en ja i u v rem e
izvršavanja.
536 Misliti na Javi

Takvo p o n ašan je nizova nije to lik o stra šn o , p o što u v rem e izvršavanja vi ip ak saznajete
d a ste u m e tn u li p o grešan tip . Ali je d a n o d o sn o v n ih ciljeva gen eričk o g k o d a jeste o tk ri-
v anje tak v ih grešaka 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 {


// Greška u vreme prevođenja: neusklađeni 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 eričk i k o d n isu sam o k o n tejn eri. G reška n a m
zap ravo kaže: „G enerički tip za d a t tipotn Jabuka n e m o žete d o d e liti g en eričk 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 „svođenje naviše“. To zap rav o i n ije „svođenje naviše“ - L ista Jabuka
nije L ista tip a Voce. Lista Jabuka m o že d a sad rži o b jek te tip a Jabuka i p o d tip o v e o d Ja-
buka, a L ista Voce m ože d a sadrži 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
sadrži. Z a razliku od nizova, generički 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 izvršavanja; 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 izvršno o k ru žen je
ne m o g u zn ati šta hoćete 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 sadrže us-
p o stav im o n ek u v rstu svodenja naviše. T om e služe 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) {
// Džokerski argumenti omogućuju kovarijansu:
List<? extends Voce> listav = new Ar r a y L i s t < J a b u k a > ( ) ;
// Greška u vreme prevođenja: nije moguće
// 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 vraća barem Voce:
Voce f = 1 i stav.get (0);
}
} ///:-
Poglavlje 15: Generički 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 znači 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 značava o d re đ e n tip , p a g o rn ji izraz znači „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 svođenje naviše n a listavbilo m og u će, taj tip je „ n ije v ažn o koji“.
U koliko je je d in o o g ran ičen je da ta L ista sadrži o d ređ e n o Voce ili p o d tip klase Voce, ali
zapravo v am nije v ažno 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 sadrži, kako d a joj b ezb ed n o d o d ate neki objekat? N e m ožete, kao što ni u p ro -
g ra m u KovarijantniNizovi.java niste m ogli da niz „svedete naviše“, sem što u o v o m sluča-
ju to sprečava prevodilac, a ta m o sistem za izvršavanje. Sada p ro b le m otk riv ate ranije.
M ožd a ste po m islili d a je ovo o tišlo p redaleko, je r sada Listi za koju ste u p rav o rekli da
sad rži 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<Narandža>. N ak o n što u ra d ite ovu v rstu „svođenja naviše“, 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 raća Voce, to je b ezb ed n o je r z n a te da
sadržaj 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.
Vežba 26: (2) P okažite k o v arijan su nizova p o m o ć u klasa N um ber i Integer.
Vežba 27: (2) P okažite 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 nači 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 eneričkog 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

važno da li ste na jp re tip Jabuka sveli naviše 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 džo 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 ači 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 hoćete 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 džokerski
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; // Svođenje naviše nije moguće
Skladiste<? extends Voce> voce = Jabuka; // OK
Voce p = v o c e . g e t O ;
d = (Jabuka)voce.get(); // Vraća 'Object'
try {
Narandža c = (Narandža)voce.get(); // Nema upozorenja
} catch(Exception e) { System.out.println(e) -, }
// voce.set(new Jabuka()); // Ne možete pozvati set()
// voce.set(new Voce()); // Ne možete pozvati set()
System.out.println(voce.equals (d)) ; // OK
}
} /* Ispis: (primer)
java.lang.ClassCastException: Jabuka cannot be cast to Narandža
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 vraća 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 naviše na Skladiste<Voce>, ali ga
m o žete svesti naviše n a Skladiste<? extends Voce>. A ko po zov ete g e t( ), o n a vam vraća
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 naviše n a o d ređ e n p o d tip klase Voce i za to nećete 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: Generički 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 znači d a m ože b iti b ilo šta, a p rev o d ilac ne m ože 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 vraćeni. O n ne
an alizira k o d kako b i v id eo d a li zaista u p isu jete ili učitavate.

Kontravarijansa
M ože se o tići i s u p ro tn im p u te m i u p o tre b iti džokerski argum ent nadtipa. Tada kažete da
je džo kersk i a rg u m e n t o g ran ičen 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 enerički 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 reći <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 generički tip. D akle, sa džo-
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()); // Greška
}
} ///:-

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
naćin 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 statičn 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 -
sleđivanja u m e to d u ) u generički tip, o d n o sn o ,,učitavanja“ (vraćanja iz m eto d e) iz gene-
ričkog tipa.
G ran ice n a d tip o v a u b lažu ju o g ran ičen 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); // Greška:


// Incompatible types: founđ Voce, required Jabuka
// (Neusklađeni tipovi: našao Voce, očekivao 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 tača n p a ra m e ta r tip a (n e m a džo 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 oguće.
U m eto d i upisivanjeSD zokerom ( ) a rg u m e n t je lista List<? super T>, p a o n a sadrži
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 ešto 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 očekivali.
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());
// Statična metoda se prilagođava svakom pozivu:
static void fl() {
Jabuka a = tacnoProcitaj(jabuke);
Voce f = tacnoProcitaj(voce);
f = tacnoProcitaj(jabuke);
}
// Međutim, ako imate klasu, njen tip se utvrđuje
// 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: Generički tipovi 541

// Voce a = citacVoca.tacnoProcitaj(jabuke); // Greška:


// tacnoProcitaj(List<Voce>) ne može 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
džo 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, statičn a generička m e to d a tacnoP rocitaj( ) zapravo se p rilag o đ av a svakom p o -
zivu i vraća 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 eričk 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 eričk 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 tačan 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 rešio 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>.
Vežba 28: (4) N aprav ite g eneričku 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 eričk u klasu Genericka2<T> čija je d in a m e to d a v raća
a rg u m e n t tip a T. N apišite gen eričku 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-
neričke klase koja poziva svoju m e to d u . N apišite d ru g u g en eričk 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 eneričke klase koja poziva svoju m e to d u . T estirajte k o d
p o m o ć u b iblio tek e pođaciotipu.ljubimci.

Neograničeni džokerski argumenti


Neogm ničeni džokerski a rg u m en t< i> p riv id n o označava „bilo šta“, p a izgleda k ao d a je ko-
rišćenje n eo g ran iče n o g džokerskog a rg u m e n ta ek v ivalen tn o k o rišćen ju sirovog tipa. Zai-
sta, p revod ilac se na prv i p o gled slaže 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 slučajeva 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 slučajevim a, m o žete sm atra ti d a je <?>
dekoracija; pa ipak, taj džokerski 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 generički m eh an iza m , i ovde n e označava da u p o tre -
bljavam n eki siro v tip, nego d a u o v o m slučaju generički p a ra m e ta r m ože da sadrži bilo
koji tip “.
Poglavlje 15: Generički tipovi 543

U d ru g o m p rim e ru p o k azan a je je d n a važna 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 više g en eričk 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,?>
// (Našao: HashMap, Očekivao: 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 džokerski a rg u m e n ti n e o g ran ičen 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 različito 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 generički a rg u m e n t
briše 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 tačna. L ist
zap ravo znači „sirova L ista koja sadrž.i bilo koji objek at tip a Object“, d o k L ist< ?> znači
„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 eđu 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 sledečem 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 različite načine: 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 ičen im đžo k ersk im a rg u m e n to m kao p a ra m e tro m :

//: genericki/Dzokeri.java
// Istraživanje značenja džokerskih 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;
)
// Slična kao siroviArgumenti (), ali prouzrokuje
// greške umesto upozorenja:
static void neogArg(Skladiste<?> skladiste, Object arg) {
// skladiste.set(arg); // Greška:
// Metoda set((rezultat od) ?) u Skladiste<(rezultat od) ?>
// ne može biti primenjena na (Object)
// skladiste.set(new DzokeriO); // Ista greška

// 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 može 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:
// Neusklađeni tipovi: našao Object, očekivao T
Poglavlje 15. Generički 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 može biti primenjena
// na (Skladiste<(rezultat od) ?>,Long)
// Long r8 = odredjena2(ograniceno, lng); // Greska:
// odredjena2(Skladiste<T>,T) ne može 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 može 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 može 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 erićk i tip, pa iako je
o vde izražen 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 . Pošto 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 naviše 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 prevođenja. 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 ližn o ista stvar. M etoda
neogA rg( ) ističe njihovu različitost - o n a otkriva istu vrstu p ro b lem a, ali ih prijavljuje kao
greške, 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 određenog tipa, i ne m ožete m u proslediti Object.
Poglav|je 15: Generički 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 generičk i p a ra m e tri


- n em a džok ersk ih a rg u m e n a ta . V idećete d a od red jen a2( ) zb og d o d a tn o g a rg u m e n ta
im a drugačija o g ran ičen 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 ičen ja tip a Skladiste u b laže n a su na Skladiste koje
p rim a sve što je izvedeno iz T. O p et, to zn ači d a T m o že biti Voce, d o k bi Skladiste m og lo
biti Skladiste<Jabuka>. D a bi se sprečilo stavljanje o b jek ta tip a N arandža 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 izađe 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 aže, p o što tip koji sad rži 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 ože 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 ožete 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/. grešaka
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 rečeno, u telu m eto d e o b ra đ u je n a različite načine.
Ako referencu sirovog tipa Skladiste prosledite m eto d i koja p rim a o d ređ en generički tip
(bez džokerskih a rg u m en ata), dob ićete u p o zo ren je, zato što o d re đ e n i arg u m en t očekuje
inform acije koje u sirovom tip u ne postoje. A ukoliko m eto d i o d red jen al() prosledite
neo g ran ičen 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 najviše o g raničen ja, p o što zahteva u p rav o Skladiste<T> i
a rg u m e n t tipa T, i zato generiše greške i u p o zo ren ja ukoliko jo j ne d ate tačn e arg u m en te.
P onekad je takvo p on ašan je d o b ro , ali ako vas previše sputava, m o žete u p o treb iti različite
džokerske 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 vašim gen eričkim a rg u m e n to m - kao što je u ra đ en o u m eto d i divljiPodtip( ) — ili
svom generičkom a rg u m e n tu hoćete 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 korišćenja o d re đ en ih (tačn ih ) tip ov a u m esto džokerskih arg u m e n a ta
jeste to što s generičkim p a ra m e trim a m o žete više d a u rad ite. Ali korišćenje džokerskih 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 slučaju saini o d lu ču jete koji k o m p ro m is v am više odgovara.

Konverzija hvatanjem
Jedna k o n k re tn a situacija zahteva da u p o tre b ite džo 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 zaključi 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 džokerski tip hvata i k o n v e rtu je u o d re đ e n tip . Pokazaćem 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 važe 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 uhvaćenim 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
// Svođenje naviše 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 džokerski 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 ičen a.
Vežba 29: (5) N apravite gen eričk 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 ožete, a koje ne m ožete pozivati za Skladiste i za Listu. Ponovite za
a rg u m e n t L ist<Skladiste<?».
Poglavlje 15: Generički 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 korišćenja gen eričk 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 ičen ja Javin ih ge-
n eričk 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), videćete 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 nadoknađuje nemogućnost
// upotrebe prostih tipova u generičkom 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 ažn 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 rešenje radi d o b ro - cele brojeve (in t) m o žete da skladištite i da ih uči-
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 ožete 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 možete:


// 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 rešava neke p ro b le m e, ali n e sve.
U sledećem p rim e ru im a m o generički in terfejs Generator koji sp e đ fic ira m e to d u
sledeci( ) koja v raća o b jek at p a ra m e ta rsk o g tipa. Klasa PNiz sadrži g eneričku 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 statična, u ovom
slučaju n e bi o d gov aralo d a klasu n a p ra v im o k ao g en eričk u ). U poglavlju N izovi naći ćete
više 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 može pomoći. Ovo se neće prevesti:
// int[] b =
// PNiz.popuni(new int[7], new RandIntGenerator());
}
) /* Ispis:
YNzbrnyGcF
0WZnTcQrGs
eGZMmJMRoE
suEcUOneOE
dLsmwHLGEa
hKcxrEqUCB
bklnaMesbt
7052
6665
2654
Poglavlje 15: Generički tipovi 551

3909
5202
2209
5458
* ///:-

Pošto 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 važi za nizove, p a to ne radi.
Vežba 30: (2) N ap rav ite Skladiste za sve om o ta č e p ro stih tipova i po kažite d a a u to m a tsk o
p ak o v an je i raspak ivanje fu n k cio n iše 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 eneričkog interfejsa. (Z bo g b risan ja, obe
p o sta ju isti interfejs.) To se d o g ađ a u sledećoj situaciji:

//: genericki/ViseVarijanatalnterfejsa.java
// {CompileTimeError} (Ne može se prevesti)

interface PlacaSe<T> {}

class Zaposleni implements PlacaSe<Zaposleni> {}


class PoSatu extends Zaposleni
implements P1acaSe<PoSatu> {} ///:-

PoSatu se neće 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 značio 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 eneričke
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.
Vežba 3 1: ( 1) U k lo n ite svu g en eričn 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 tiču na p a ram etre g eneričkog tip a. Sledeći
k o n te jn e r in te rn o skladišti 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 ože 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 briše 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 slučajeva kada g enerički 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 sledećem poglavlju, m e to d a readO bject( ) ne m o že znati šta čita,
pa vraća o b jek at koji se m o ra ko n v erto vati. Ali k ad a k o m e n ta riše te a n o ta c iju @Suppress-
W arnings i prev ed ete p ro g ra m , d o b ićete u p o zo ren je:

Note: PotrebnaKonverzija.java uses unchecked or unsafe operations.


Note: Recompile with -XIint:unchecked for details.
Poglavlje 15: Generički 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 kažu da ne b i trebalo. D a biste rešili 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 generičke 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]));
// Neće 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
rečim a, ne m o žete reći:

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.


Vežba 32: (1) U verite se da StekNeproinenljiveVelicine u p ro g ra m u KonverzijaGene-
rickih.java g eneriše izuzetke kada p o k u šate d a izađete izvan n jegovih granica. D a li to
znači da ne m o ra te pisati k o d za p ro v eru granica?
Vežba 33: (3) P opravite p ro g ram KonverzijaGenerickih.java p o m o ć u klase ArrayList.

Preklapanje
O vo se neće prevesti, iako bi se reklo da valja p o k u šati:

//: genericki/ ListaZallpotrebu.java


// {CompileTimeError} (Neće 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 različita 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) {}
} ///= -

Srećom , 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 ože 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 može se prevesti)

class Macka extends UporedivLjubimac implements Comparable<Macka>{


// Greška: interfejs Comparable ne može se nasleđivati
// s različitim argumentima: <Macka> i <Ljubimac>
public int compareTo(Macka arg) { return 0; }
} ///:-

N ažalost, ovo neće 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 neće m oći da se p o red i ni sa čim
d ru g im sem sa o bjektim a klase UporedivLjubimac:
Poglavlje 15: Generički 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 tičan , 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.

Samoograničeni tipovi
U Javino m g eneričk 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 enerički a rg u m e n t T, T je o g ran ičen
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 teško analizirati kada ga p rv i p u t vidite; n jim e se ističe 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 ačija n ego kada se k o risti za
pravljen je potklasa.

Generički kod koji se neobično ponavlja


D a biste shvatili šta znači 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 ičen ja.
G en erički p a ra m e ta r ne m o žete naslediti d ire k tn o . M eđ u tim , m ožete d a n asledite kla-
su koja taj generički 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 rečim a,
m ožete reći:

//: genericki/GenerickiTipKojiSamSebePonavlja.java

class GenerickiTip<T> {}

public class GenerickiTipKojiSamSebePonavlja


extends GenerickiTip<GenerickiTipKojiSamSebePonavlja> {} ///:-
556 Misliti na Javi

O vo b ism o m ogli nazvati generički tip koji se neobično ponavlja ( Curiously Recurring
Generics, CRG), p o C o p lien o v o m Šablonskom obrascu koji se neobičnoponavlja 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 ičn o , pojavljuje
u so pstvenoj o snov n o j klasi.
D a biste shvatili šta to znači, naglas izgovorite: „P ravim n o v u klasu koja nasleđuje generi-
čki tip koji im e m oje klase u zim a za svoj p a ra m e ta r“. Sta generički osnovni tip m ože da u ra-
di s d a tim im e n o m izvedene klase? Pa, u Javi se generički 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 ože 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 ože da upo treb i za tipove
polja, iako će o n i brisan jem b iti svedeni n a Object. To izražava sledeća generička 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 generički 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 išten 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 eričk o m tip u koji se n eo b ičn 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 nečem u važn 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 znači da generička osnov-
na klasa p o staje neka vrsta šab lo n a za zajedničku fu n k c io n a ln o st svili svojih izvedenih
Poglavlje 15: Generički 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 rečim a, u rezultujućoj klasi se u p o treb ljav a tačan, 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.

Samoograničenje
OsnovnoSkladiste m o ž e d a upotrebi bilo koji tip k a o svoj generički 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 iče n je o b u h v ata i d o d a tn i k o rak kojim se nameće da generički 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 ičen 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> {} // Takođe OK

class C extends SamoOgraniceni<C> {


C setIGet(C arg) { set(arg); return get(); }
}

class D {}
// Ovo ne možete:
// class E extends SamoOgraniceni<D> {}
// Greška u vreme prevođenja: parametar tipa D nije unutar svojih granica
558 Misliti na Javi

// Ovo nažalost možete da uradite, što znači da


// ovaj idiom ne možete 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 ičenje zahteva d a se klasa u p o tre b i u o v akvom o d n o su nasleđivanja:

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 definišete.


Š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 definiše. 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 najčešća o n a
u p o treb a koju vid ite za klasu A. P okušaj d efin isan ja klase E p o k azu je da ne m ožete up o -
treb iti p a ra m e ta r tip a koji nije SamoOgraniceni.
N ažalost, 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 ože na-
m e tn u ti. A ko je to zaista važno, 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 ičen je m o žete u k lo n iti i sve će se klase prevesti, ali isto važi 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: Generički tipovi 559

class D2 {}
// Ovo je sada ispravno:
class E2 extends NijeSamo0granicena<D2> {} ///:-

D akle, o g ran ičen je sa m o o g ra n ič en jem služi sa m o da n a m e tn e o d n o s nasleđivanja.


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 ičen je se m ože u p o tre b iti i za generičke 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 sprečavate da m e to d a b u d e p rim e n je n a n a išta d ru g o d o na sam o o g ran ičen 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 ičav 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 važn 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 redefiniše m e to d u d a jo b ( ) interfejsa


ObicnaDajOb i vraća tip izveden iz tip a koji vraća O bicnaD ajO b.dajob( ). lak o je to sa-
svim logično - treb alo bi d a m e to d a izvedenog tip a m o že da v ra ć a specifičniji tip o d m e-
to d e o sno v nog tip a koju redefiniše - 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 ičen 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(); // Takođe 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 eričk 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); // Prevešće se: preklopljena je,
// nije redefinisana!
Poglavlje 15: Generički 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 redefiniše 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 ičav 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); // Pogrešno:
// zadajOb(ZadajOb) u SamoOgranicenaZadajOb<ZadajOb>
// ne može biti primenjena na (SamoOgranicenaZadajOb)
}
} III--
P revodilac ne p rizn aje p okušaj 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 ičav an ja, uskače u o b ičajen 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 eričk o m slučaju:

/ / : genericki/ObicnoGenerickoNasledjivanje.java

class GenerickaZadajOb<T> { // Nije samoograničena


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); // Prevešće se: preklopljena je,
// nije redefinisana!
}
} /* Ispis:
IzvedenaZO.zadajOb(Izvedena)
GenerickaZadajOb.zadajOb(Osnovna)
* ///:-

O vaj k o d po d ražav a p ro g ra m ObicniArgumenti.java; u to m p rim e ru , Izvedena-


ZadajOb nasleduje klasu ObicnaZadajOb koja sadrži svoju m e to d u zadajOb(Osnovna).
O vde, IzvedenaZO nasleduje k lasu GenerickaZadajOb<Osnovna> koja tak o đ e sadrži
svoju m e to d u zadajOb(Osnovna) a n ju je n ap rav ila sp o m e n u ta generička klasa. Kao u
p ro g ra m u ObicniArgumenti.java, iz rezu ltata v id ite da IzvedenaZO sadrži dve p re-
klo p ljen e verzije m eto d e zad ajO b ( ). Bez sam o o g ran ičav an ja, tipove a rg u m e n a ta p rekla-
pate. A ko upotre'oite sam o o g ran ičav 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 tačn o g (o d ređ en o g ) tipa.
Vežba 34: (4) N apravite sam o o g ra n ič en gen eričk i tip koji sadrži a p stra k tn u m e to d u koja
p rim a a rg u m e n t tip a generičkog 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-
neričk 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.

Dinamička bezbednost tipova


Pošto generičke kontejn ere m ožete prosleđivati 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 vaše kontejn ere. Java SE5 u pak etu java.util.Col-
Iections im a skup uslužn ih m eto d a za rešavanje pro b lem a s pro verom tipova u toj situaciji:
statičn 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 hoćete
da d in am ičk i ispitate kao svoj p rv i a rg u m e n t i tip koji hoćete 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-generičk og (sirovog) kon-
te jn e ra koji bi vas obavestio da po stoji p ro b lem p rilik o m vađenja objekta. U to m slučaju
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 varajući objekat.
R azm o trićem o p ro b lem „stavljanja m ačke 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 sprečilo
rezu ltu ju će upozorenje:
Poglavlje 15: Generički 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); // Generiše 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: Pokušaj umetanja klase
podaciotipu.ljubimci.Macka u kontejner ćiji tip određuje klasa
podaciotipu.ljubimci.Pas)
* ///:-

Kada p o k ren ete p ro g ra m , videćete 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 generiše 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 ežba 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 eričk 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 ože da hvata izuzetke generičkog tipa, zato što tača 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 izvršavanja. Takođe, generička 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 sprečava d a definišete generički izu-
zetak koji se ne m ože 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 išite g en erički 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: Generički 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 ešta se u List<T> kontejnerRezultata (to nazivam o param etar kolekci-
je). Klasa PokretacO brada im a m e to d u obradiSve( ) koja izvršava sve o bjek te tipa
O brada koje klasa sadrži i vraća 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
generički, i to zbog prov erav an ih izuzetaka.
Vežba 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 ažite 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 značenja, ali o sn o v n o je: m ešanje
m o g u ć n o sti više klasa da bi se d o b ila rezu ltu ju ća klasa koja pređstavlja sve um ešan e tip o -
ve, tj. o n a se naziva m ešavina ili m iksin. To se o b ičn 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 ašan ja d o sled n o p ri-
m e n ju ju na razn e klase. Uz to, k ad a nešto 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 ačajan d eo aspektno
orijentisanogprogram iranja (A O P), a za rešavanje p ro b le m a s m ešav in am a često se p red -
lažu razni aspekti.
566 Misliti na Javi

Miksini ujeziku C++


Jedan o d n ajjačih razloga za u v o đ e n je v išestru 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 služi 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 statične 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: Generički 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 eša-
n ih tipova. S m atrajte m ik sin fu n k c ijo m koja preslikava p o sto jeće klase na nove potklase.
O b ra tite p ažn ju na to kako se p o m o ć u ove teh n ik e lako prave m iksini; u suštini, vi sam o
kažete: „Evo šta h o ć u “, i to se izvrši:

SVremenskomOznakom<SaSerijskimBrojem<0snovni> > miksinl, miksin2;

N ažalost, Javin generički 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 generička klasa ne m o že d ire k tn o d a nasled i generički param etar.

Mešanje pomoću interfejsa


Č esto se pred laže d a se efekat m ik sin a p o stig n e p o m o ć u interfejsa, i to n a ovaj način:

//: 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 napišete 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 ežba 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 ažite da radi.

Korišćenje obrasca Decorator


Kada p o gledate način 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 ičajeno pravljenje potklasa daje toliko kla-
sa da postizanje svih m ogućih k om binacija postaje n ep rak tičn o , često se koriste dekoratori.
O b ra z a c Decorator opisu je korišćenje 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 ačn o . Decorator specificira d a svi o b jek ti koji se
o m o tav a ju oko vašeg 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 ože 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 rišu 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: Generički tipovi 569

svojstvo „m ože 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 rišćen je d e k o ra to ra p o sta je nev id -
ljivo - p o sto ji sk u p zajed n ičk 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 eričk 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 nasleđivanja p ro je k tn o g o b ra sc a Decorator.
N apisaćem 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 rišćen jem m ik sin a sadrži 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 rečim a, iako je moguće d o d ati više 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 značajan 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 rišćen 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 rešava p ro b le m koji p o tp u n o rešavaju m iksini.
V ežba 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 dinamičkim 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 jašn 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 , dinatnički 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 ičen ja k o jim a p o d ležu d in a m ičk i p o sred n ic i, svaka u m ešan 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: Generički 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 statičn o g tip a, sam o d in am ičk i tip sadrži sve u m ešan 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 iže 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 bliže 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 riličn 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.
Vežba 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 ešajte je u m iksin i d okažite 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 najvećoj m o g u ćo j m eri. D a b ism o to p o stig li, m o ra m o u blažiti o g ran ičen 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 išem o k o d koji se bez iz m e n a m o ž e u p o treb ljav ati u više si-
tuacija - tj. opštiji kod.
Izgleda d a Javin generički k o d p rav i jo š je d a n k o ra k u to m sm eru . Kada pišete ili k o ri-
stite generičk 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 izglađuje). Ili, d ru g im rečim a, g eneričko skladište
m ože da kaže: „M eni je svejedno koji si tip “. K od k ojem nije važno s ko jim tip o v im a radi,
zaista se m ože p rim e n iti sv u d a i stoga je p o tp u n o o p šti (g enerički).
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 generičke tipove
(ukoliko se to n e m ože 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 generičkih 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 generičke o b jek te u vašem k o d u . To je zn ačajno ograničenje
p o jm a opštosti, p o što svoje generičke tipove m o ra te o g ran ičiti tako da n asleđ u ju o d ređ en e
klase ili realizuju o d ređ en e interfejse. U n ek im slučajevim a, m o žd a ćete u m esto generičkih
klasa i interfejsa u p o tre b iti o b ičn e klase ili interfejse, p o što se o g ran ičen i generički tip ne
m o ra razlikovati od specificiranja klase ili interfejsa.
N eki p ro g ra m sk i jezici to rešavaju 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 zvuči 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 vuče istorijski p rtljag kao p re th o d n a dva te rm in a.
G enerički k o d o b ičn o poziva tek n ekoliko m eto d a za o d re đ e n generički tip, a jezik s la-
te n tn im tip o v im a ublažu je to o g ra n ič e n je (i daje o pštiji 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 ičkog interfejsa. Stoga parče k o d a m o že zap ravo reći: ,A ko m ožeš 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 opštiji.
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 korišćenje koda. V išekrat-
n o u p o treb ljiv k o d je lakše p isati s n jim a nego bez njih . R aspoređivanje i p o n o v n o ko-
rišćenje koda jesu o sn o v n i p rin c ip i sveg p ro g ra m ira n ja : napiši k o d je d a n p u t, koristi ga
više p u ta i čuvaj n a je d n o m m estu . Pošto 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 pišem m an je k oda i d a ga lakše p ri-
m en ju jem na više m esta.
O d jezika koji p o d ržav 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 ičk i (gotovo sve
p ro veravanje tipov a obavlja se u v rem e izvršavanja), a C + + statički (u v rem e prev o đ en ja).
D akle, late n tn i tip ovi ne zah te v aju n i statičko ni d in am ič k o p ro vcravanje tipova.

6 L atentne tipove podržavaju i jezici Ruby i Sm alltalk.


Poglavlje 15: Generički 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 važenja na o sn o v u u v u čen o sti o d leve m arg in e (p a vitičaste


zag rad e nisu p o tre b n e ), a d v o tačk o m označava p o četa k nove oblasti važenja. Z n ak o m #
ozn ačava 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 reči new. U jeziku P y th o n m o g u se
ko ristiti i o b ičn 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 ažn ju na to d a bilosta n em a tip i d a je sam o identifikator.
Pošto 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 išete —
o n je latentan. Funkciji o b a v i( ) nije v ažno koji je tip n jen o g a rg u m e n ta , p a joj se m ože
p ro sled iti svaki o b jek at koji p o d ržav 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 ržav a te o p eracije, d o b ićete izuzetak u v rem e izvršavanja.
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 išta zajedničko, 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 išta tipova, to su sasvim različiti tipovi. M eđ u tim ,
funkciji o b a v i ( ) nije važno 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 -
gućuje da p rih v ati objekte o b a tip a.
C + + p ro verava m o že li zaista d a pošalje te p oruk e. A ko p o ku šate d a prosled ite p o -
grešan tip , p rev o dilac će v am ispisati p o ru k u o grešci (te p o ru k e o greškam a su o d uv ek
bile užasne i o p širn e, i o sn o v n i su razlog što C + + -o v i šab lo ni im aju iošu rep u taciju ). Iako
to rad e u različita v re m en a - C + + u v rem e p revo đen ja, a P y th o n u vrem e izvršavanja -
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 grožavaju stro g u tip iziran o st.
Pošto su g enerički 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 izgleđa kao da je Ja-
vin generički m eh a n iza m „m an je generičk i" o d jezika koji p o držav 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 raćem 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 išta . V ero v a tn o je ta č n ije reći 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^i’ni.'tfthii ^ e n e rić k i tip o v i.
Poglavlje 15: Generički 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 enerički 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 generički 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 slučaju, generički 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 ržav a la te n tn e tipove, ispostavlja se d a to n e zn ači d a se generičk i k o d sa
o g ran ičen jim a n e m ože 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 rečim a,
ip ak je m oguće pisati pravi generički k o d , ali je to n ešto teže.

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 pomoću 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. Generički 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 može govoriti");
}
try {
Method sedeti = zvck.getMethod(“sedeti");
sedeti.invoke(zvucnik);
} catch(NoSuchMethodException e) {
print(zvucnik + 11 ne može 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 može govoriti
Pravim se da sedim
* ///:-

O vde su klase sasvim različite i n em aju zajed n ičk 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 ože di-
nam ički 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 ože 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 ično.

Primena metode na sekvencu


Refleksija p ru ža zanim ljive m o g u ćn o sti, ali svu p ro v eru tipova odlaže za tre n u ta k izvrša-
vanja i zato je nepoželjna u m n o g im situ acijam a. P rovera tip o v a u v rem e prevođenja
o b ičn o je m n o g o poželjnija. Ali, da li je m o g u će im ati p ro v e ru tip o v a u v rem e prevođenja
i laten tn e tipove?
578 Misliti na Javi

P ogledajm o p rim e r koji istražu 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 ičen je koje ne dozvoljava d a o pišete „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 rešim 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) {
// Greške 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: Generički 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 sreće, 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 ože 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 ože 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
// Drugačija 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 slučaju 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 džokerske 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, videćete d a Prim eni i PopunjenaLista neće 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 položaj. 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 izvršavanja.
O b ič n o se p red laže 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 đ ača" 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 ažalost, većina 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 rešen je ovog p ro b le m a p o m o ć u ad ap te ra.
N o, m ožd a je p rik azan i p ristu p korišćenja leksem e tip a razb o rit k o m p ro m is (b arem
kao p rv o rešenje). Uz takav p ristu p , korišćenje nečega kao što je objek at tip a Popunjena-
Lista dov o ljn o je lako da o n biva korišćen, a ne zaobiđen. N aravno, greške se p rijavljuju tek
u v rem e izvršavanja, 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-
rički tipovi u program skom jeziku Java,9 gde auto r, G ilad B racha, kaže: „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 srećni 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 rešenje 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 ože 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 ljša n o ) o d realizacije bez refleksije, p o što se u v rem e izvršavanja dešava m n o g o toga. To
ne bi treb alo da vas spreči da k o ristite ovo rešenje, b are m za p o četa k (ukoliko ne p a d n e te
u iskušenje 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.
Vežba 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 slučajno nemate odgovarajući interfejs


Im ali sm o sreće 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 tačno
o n o što n a m treb a. A šta da ra d im o u o p šte m slučaju, kada n em a već u g rađ en o g interfejsa
koji slu čajn o baš zadovoljava naše 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 kušajte da nap išete to u Javi i n aletećete 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: Generički 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 kažete: „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 jući k o d nije n a ro čito opšti, p o što m o ra biti o g ran ičen 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 eričk i k o d neće rad iti. Evo k ako to izgleda:

//: genericki/Popuni .java


// Uopštavanje 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 slučaju, ne možemo da
// uopštimo pomoću generičkog 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>();
// Neće raditi. popuni() nije dovoljno opšta:
// 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 poslužio,


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 pređvidela vašu
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 ičen 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(). Pošto je
o graničen 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 naročito
opšti. To ne bi bilo tako d a im a m o la te n tn e tipove.

Simuliranje latentnih tipova pomoću adaptera


Dakle, Javin generički 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 išem o p ro g ram e koji se m o g u p rim e n iti na najrazličitije klase (tj. opšte (generičke) p ro -
gram e). M ožem o li nekako da z a o b id e m o to ograničenje?
Šta bism o ovde postigli p o m o ć u la te n tn ih tipova? M ogli b ism o da n ap išem o kod koji
kaže: „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 sadrži željene m etod e. Iz toga sledi da će p ro b lem biti
rešen ako sam i n ap išem 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 stojećih
klasa tako da n aprave željeni interfejs, uz relativn o m alu količinu koda. Rešenje 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 različite načine
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: Generički 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 prilagođavanje osnovnog tipa morate upotrebiti kompoziciju.


// Pomoću kompozicije, napravićemo 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 pri1agođavanje određenog tipa možete upotrebiti nasleđivanje.


// Pomoću nasleđivanja, napravićemo 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);
// Pomagačka metoda tip hvata:
Popuni2.popuni(Adapter.adapterKolekcije(nosilac),
SMlekom.class, 2);
584 Misliti na Javi

for(Kafa c: nosilac)
print(c);
print("................. ...... ");
// Upotreba prilagođene 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 ešto š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 neće
p u stiti da mu prosled ite n eisp rav an tip G eneratora, pa u v rem e izvršavanja neće biti
izuzetaka.
Prvi adapte r, ImaAddCollectionAdapter, rad i sa o sn o v n im tip o m Collection, što
znači da se m ože u p o tre b iti svaka realizacija klase Collection. O va verzija je d n o sta v n o
skladišti 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 nasleđi-
van jem zahtevaće nešto 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 eričk 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 izričito 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 raču n a o to m e da u oba slučaja 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: Generički tipovi 585

Izgleda kao d a ovakvo k o rišćen 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 neće 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 olakšavaju ko-
rišćenje generičk og koda, i u to m e je n jih o v a v red n o st.
Vežba 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 ršn o m p rim e ru n a p ra v iće m o p rav i g enerički 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 izvođenja o p štih o p e ra -
cija ko rišćen 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, videćete d a u to m sluča-
ju im a m o zajedničke 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 slučajevim a m o že p o sto jati neka v rsta m eto d e add. To je u o b iča-
jen a situ acija koja se sreće kada p o k u šav ate d a pišete generički k o d , zato što h o ćete da k o d
b u d e p rim e n ljiv na različite klase - n aro čito , kao u o v o m slučaju, n a više posto jećih klasa
koje ne m ožete 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 išta n e kaže o nekoj sabirljivosti.
Rešenje 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 ačin 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 ržav 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 izgleđa kao o b ičan 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 rosleđivati, 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, nešto slično m o žete postići s bilo ko jo m
m e to d o m o d re đ e n e klase, ali (kao što je slučaj 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 ešto što se p o -
naša kao m eto d a koja se m ože p rosleđivati; 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 više p ro je k tn ih o b razaca, ovde m i reči 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.
Sleđeći taj p ristu p , d o d a o sam razn e vrste g en eričk 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 ošto „funktor" ima
specifično i d ru g o značenje 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 tekućim rezultatom, koji zatim vraća:
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 generiši izuzetak
}
// Uzmi funkcijski objekat i pozovi ga za svaki objekat u
// listi; povratnu vrednost zanemari. Funkcijski objekat
// može delovati kao parametar kolekcije, pa se
// na kraju on vraća 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 vraća 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: Generički tipovi 587

for(T t : sekv)
if(pred.test(t))
rezultat.add(t);
return rezultat;
}
// Da bismo mogli da koristimo gornje generičke metode,
// moramo napraviti funkcijske objekte za prilagođavanje metoda
// našim specifičnim 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()));
}
}
// Možemo 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) {
// Generički tipovi, argumenti promenljive dužine
// i pakovanje u zajedničkom 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 ugrađeno 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: Generički 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 različite tipove fu nkcijskih objek ata. N jih sam
prav io p o p o treb i, kako sam razvijao različite 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 ožete 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 ožete da izdvojite kada završite. 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 sadrži više g eneričkih 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 ožete 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 ože da po služi), ili Kolektor m ože da
o d ržava in te rn o stan je d a bi p o stao p a ra m e ta r kolekcije, kao što je slučaj 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 raćaju tru e sm ešta u Listu koju vraća.
M ožete definisati i sopstvene generičke 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 rešen 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 eričk i alg o ritm i za Javu).
C + + -o v i ia te n tn i tipovi se staraju za pronalaženje odgo varaju ćih op eracija p rilik o m p o -
ziva funkcija, ali u Javi m o ra m o d a p išem o funkcijske objekte za prilago đav anje generičkih
m eto d a našim p o treb am a. Z ato sledeći deo klase sadrži različite realizacije funkcijskih
objekata. O b ra tite pažn ju n a to, recim o, da SabiracCelih i SabiracVelikihDecimalnih
rešavaju 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 akođe, više izraza je p rilič n o složeno, 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 većih 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 eću da o bjašn ja-
v am - v e ro v a tn o ćete ih i sam i shvatiti k ad a ih p ro u čite.
Vežba42: (5) N ap rav ite dve zasebne klase koje n em aju ništa zajedničko. Svaka klasa treb a
d a sadrži 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 vaših
klasa (te o p eracije ne m o ra ju b iti aritm etičk e kao što su u p ro g ra m u Funkcional.java).

Sažetak: da li je eksplicitna konverzija tipova zaista


tako loša?
Pošto C + + ša b lo n e objašn jav am o d njih o v o g n astan k a, sleđeće ra z m a tra n je sam n av od io
vero v atn o češće 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 generičkih 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 naviše na Object, pa se gubila in-
form acija o p ra v o m tip u . K ada je treb alo nešto 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 eneričke 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: Generički tipovi 591

M eđ u tim , p red g en eričk 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 izvrša-
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 više od neprijatnosti. Zbog toga mogu nastati greške k o jeje teško otkriti. Ako deo
(ili više delova) program a umeće 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
tražite gde je loš objekat umetnut.
N akon daljnjeg razm išljan ja o ovom e, p očeo sam d a su m n ja m . P rvo, koliko često se to
događa? Ne sećam se d a m i se ikada desilo n ešto 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
sadrži 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 loše n a p isa n p ro g ra m
koji se ipak prevede i dalje ostaje loš. M ožda većina 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 počelo 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 greška m o g la o sta ti „sakrivena g o d in a m a “. Ali
nešto ne p a m tim p oplav u izveštaja o lju d im a koji su v eo m a teško p ro n alazili greške tip a
,,pas u listi m ačaka“, pa čak se ne sećam ni da su ih često pravili. S d ru g e stran e, u poglavlju
Paralelno izvršavanjc videćete d a se s n itim a v eo m a lako i često p rave greške 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
greška ,,pas u listi m ačaka“ pravi razlog što je ova veo m a zn ačajn a i p riličn 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 jezičke m o g u ćn o sti o p šte n am en e n azv an e „generički tip o v i“ (n e
n u žn o i njene k o n k re tn e realizacije u Javi) izražajnost, 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 opštije nam en e.
Z ato, iako se greška tip a „pas u listi m ačaka“ često navodi kao o p ra v d an je za uvo đ en je
generičk 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 eneričkih tipova. Kao što im i im e govori, generički
tip ov i o m o g u ć u ju pisan je opštijeg k o d a koji m an je og ran ičav a tipove s k ojim a m ože da
radi, pa se isto parče koda m ože p rim e n iti na više tipova. U o v o m poglavlju videli ste da
je p riličn o lako nap isati zaista opšte klase ,,skladište“ (a Javini k o n te jn e ri to jesu ), ali na-
pisati gen erički kod koji o b ra đ u je svoje generičke 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 otežava korišćenje gen eričk ih tip o v a i čini ih m an je p rim e n lji-
vim u slučajevim a gde bi inače d o b ro poslužili.
592 Misliti na Javi

T akođe, im ajte u v id u sledeće: zbog to g a što su generički tip o v i bili n a k n a d n o ub ačen 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 jećim
gen eričk 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 enerički 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 ključa se uvek p roverava u v rem e
prev ođ en ja.
Jedno je sasvim jasno: uvo đ en je bilo koje v rste generičkog m e h a n iz m a u kasnoj verziji
jezika, n ak o n što je o n ušao 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 ože 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 poteškoće, 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 generički 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 generičke 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 uključe u jezik, ali su iz nekog razloga (izgleda da su žu-
rili) odlučili 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 eneričkih 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 generiše
Javin b ajtkod i radi s p o sto jećim Javinim b ib lio tek am a) i N extG en (videti http://ja-
pan.cs.rice.edu/nextgeri) im aju čistiji i lakši p ristu p p a ra m etriz o v an im tip o v im a. Nije ne-
m o guće 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 postojeće i to poboljšali.

Pročitajte i ovo
U vodni d o k u m e n t za g eneričke 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.
Više o d žo k ersk im a rg u m e n tim a saznaćete 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.
Rešenja odabranih vežbi data su u elektronskom dokum entu l'lic Thiitking in lciva Annotatctl Soltt-
tion Guidekoji se može kupiti na lokaciji wmv.MiiidVicw.coiu.
Nizovi
N a kraju poglavlja Inicijalizacija i čišćenje objašnjeno j e kako :,c definiše 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 više d ru g ih n ačin 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 ičan 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 plaća: kada n a p rr . : niz, njegova veličina je
ta č n o o d re đ e n a i ne m ože se m en jati to k o m celog njegovog . o tn o g veka. Kao rešenje
ovog p ro b le m a m ože 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 veličine o setn o m an je efikasna o d
o b ičn 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, pojaviće se izuzetak tip a R u n tim e E x c e p tio n ako im p rek o račite granice,
što ukazuje na grešku p ro g ram era.
Pre generičk 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 predgeneričk i k o ntejnera: kada p ravite
niz, zn ate da će o n sadržati o d ređ en i tip. To znači 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 rešn o g tipa u niz, ili čitanje pogre. g tipa iz niza. N aravno,
Java će sprečiti i slanje n eo dgovarajuće p o ru k e o bjek tu , i to u v p 'm e prev o đ en ja ili izvrša-
vanja. D akle, nijedan o d dva naćina nije rizičniji. Lepše je da prevodilac u p o z o ri na
grešku u p ro g ra m u , pa da krajnji korisnik ne b u d e iznenađen ■ig nekog izuzetka.
N iz m ože da čuva pro ste tip o v e ,d o k predg en erički ko n tejn eri to nisu m ogli. M e đ u tim ,
generički k o n tejn eri mogu da zađaju i proveravaju tip objekata koje sadrže, 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 generički 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 ličnost 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 rešavate 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 ičen ja, i u tim
slučajevim 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 rži 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 ožete d a p ristu p ite ) jeste član le n g th koji se m o že sam o čitati, a ozn ačav 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.
Sledeći p rim e r p rik azu je različite 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. Jeđina razlika je to što nizovi o b jek ata sadrže reference, d o k nizovi p ro -
stih tipo v a sadrže 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) // Moguće testiranje na null referencu
c[i] = new SferaOdBeri 1 ijuma();
// Agregatna inicijalizacija:
SferaOdBeri1ijuma[] d = { new SferaOdBeri1ijuma(),
new SferaOdBeri1ijuma(), new SferaOdBeri1ijuma()
};
// Dinamička agregatna inicijalizacija:
a = new SferaOdBeri 1 ijuma[] {
new SferaOdBerilijuma(), new SferaOdBeri1ijuma(),
};
// (Zarez na poslednjem mestu je neobavezan u oba slučaja)
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 };
// Greška u prevođenju: 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 sprećava 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 eštaju 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 postojeći objekat. O vde se pojavljuje mali n ed o statak : ne m ožete da sa-
zn ate koliko se stv arn o elem en ata nalazi u nizu, p o što polje le n g th saopštava sam o koliko
elem enata možc da stanc u niz, tj. kolika je d u žin a niza, a ne koliko elem en ata sadrži. 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 ožete da u tv rd ite da li o d red en i eiem en t niza sadrži o b jek at tako što p ro v erite da li je
njegova v red n o st null. Slično 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 eričk 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 sledećeg niza se m ože o zn ačiti 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 sledeći način:

sakrij(d);

ali m o žete i d in am ičk 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 ačin 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 ože d o d eliti d ru g o m nizu,


baš kao što se m ože 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-
našaju isto kao i nizovi o b jekata, osim što nizovi p ro stih tip o v a d ire k tn o sad rže v red n o sti
p ro stih tipova.
V ežba 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 ičk i. Pokažite da u to m sluča-
ju o b ičn a ag reg atna inicijalizacija nizova ne radi. P ro n ađ ite jed in e situacije u kojim a
o bična agreg atn a inicijalizacija nizova radi, i o n e gde je re d u n d a n tn a .

Vraćanje niza vrednosti


P retp o sta v im o da pišete m eto d u koja ne treb a da v rati je d n u v red n o st, već više 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 teško k o n tro lisa ti živ o tn i vek niza, što
p ro u z ro k u je neefikasno korišćenje m em o rije.
U Javi m ožete 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 završite, o b risaće ga saku pljač sm eća.
598 Misliti na Javi

Kao p rim er, po g led ajm o kako se v raća niz e lem en ata tip a S trin g :

//: nizovi/Sladoled.java
// Vraćanje 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", "voćna 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, voćna pita]
[vanila, čokolada, moka]
[praline, jagoda, moka]
[moka, jagoda, mentol]
* ///:-

M etoda skupUkusa() pravi niz elem en a ta tipa String nazvan rezultati. D užin 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 ičn o se biraju ukusi
iz niza UKUSI i stavljaju u niz rezultati koji se vraća kao rezultat m eto d e. V raćanje niza
je isto kao vraćanje bilo kog o b jek ta - rad i se o referenci. Nije v ažno 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 eća se b rin e o
b risan ju niza kada se završi 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 ično bira ukuse, obezbeđuje da isti elem ent ne bude
izabran dvaput. To se radi u petlji do koja bira nasum ično sve d ok ne pronađe neku vred-
nost što se još ne nalazi u nizu izabran. (Naravno, moglo je da se obavi i poređenje vred-
nosti objekata klase String da bi se ustanovilo da li se izabrani elem ent već nalazi u nizu
rezultati.) Ako pronađe odgovarajući element, dodaje nov ukus i traži sledeći (i se pove-
ćava za jedan).
Iz rezultata vidite da skupUkusa() svaki p u t bira ukuse slučajnim redosledom.
Vežba 2: (1) Napišite m etodu koja prim a int argum ent i vraća niz te veličine, popunjen
objektim a SferaOdBerilijuma.

Višedimenzionalni nizovi
Lako je praviti višedim enzionalne nizove. Za višedim enzionalni niz prostih tipova, svaki
vektor niza pišete u n u tar vitičastih zagrada:

//: ni zovi/Vi sedimenzionalni Ni zovi Prosti h .java


// Pravljenje višedimenzionalnih 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 ugneždenim skupom vitičastih zagrada prelazite na sledeći nivo niza.


U ovom prim eru upotrebljena je m etoda A rrays.deepT oString() Jave SE5, koja više-
dim enzionalne nizove pretvara u znakovne nizove (objekte tipa String), kao što vidite iz
rezultata.
Niz možete napraviti i pom oću rezervisane reči 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 dužine:
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 sačinjavaju m atricu m ože biti proizvoljne dužine (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 različite dužine:
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 ično odabrane dužine, a ostali elem enti
neodređene dužine. Drugi new unutrašnje petlje for popunjava elemente, ali treći indeks
ostavlja kao neodređen do trećeg new.
Na isti način m ožete praviti nizove neprostih objekata. Ovde ćete videti kako sm o više
new izraza okupili u n u tar vitičastih 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 dužine svih lista objekata različite.
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 parče po parče pravi niz neprostih objekata:

//: nizovi/SastavljanjeVisedimenzionalnihNizova.java
// Pravljenje višedimenzionalnih 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 služi samo za to da se nešto zanimljivo upiše 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
// Višedimenzionalni nizovi "omotačkih" 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 otačke
objekte um esto nas.
Vežba 3: (4) Napišite m etodu koja pravi i inicijalizuje dvodim enzionalni niz tipa double.
Veličinu niza ocireduju argum enti m etode, a inicijalizađone vrednosti su date opsegom
o dredenim početnom i završnom vrednošću 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 različitih veličina.
Vežba 4: (2) Ponovite p rethodnu vežbu za trodim enzionalni niz.
Vežba 5: (1) Pokažite da se višedim enzionalni nizovi neprostih objekata autom atski ini-
cijalizuju na null.
Vežba 6: (1) Napišite m etodu koja prim a dva int argum enta koji pokazuju odgovarajuće
dim enzije 2-D niza. M etoda treba da napravi i popuni 2-D niz SferaOdBerilijuma u skla-
du sa argum entim a dimenzija.
Vežba 7: (1) Ponovite p rethodnu vežbu za 3-D niz.
Poglavlje 16: Nizovi 603

Nizovi i generički tipovi


Po pravilu, nizovi i generički tipovi ne idu zajeđno. Nije m oguće 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 tačan tip koji sadrži,
da bi rad s tipovim a proveravanjem učinio bezbednim .
M eđutim , m ožete 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 računa 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 različite tipove na
koje ćete je prim eniti, i možete da je napravite statičnom . N aravno, ne m ožete uvek da
upotrebite param etrizovanu m etodu um esto param etrizovane klase, ali to bi m oglo da
bude bolje rešenje.
Nije najtačnije reći da je nem oguće napraviti niz generičkog tipa. Istina je da prevodi-
lac neće dozvoliti pravljenje instance niza generičkog tipa. M eđutim , dopustiće da napra-
vite referencu na taj niz. Na primer:

List<String>[] ls;
604 Misliti na Javi

Ovo će proći kroz prevodilac bez problem a. I m ada ne možete napraviti objekat niza koji
sadrži generički tip, možete napraviti niz negeneričkog tipa i eksplicitno ga konvertovati:

//: ni zovi/Ni zGeneri cki hTi p o v a .java


// Moguće je napraviti niz generičkog 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 prevođenja daje grešku:
//! 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 izvršava bez pritužbe:
o b j e k t i [1] = new Ar ra yL i s t < I n t e g e r > ( ) ;

// Međutim, za jednostavne potrebe moguće je


// napraviti niz generičkih 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-
vođenja. Problem je to što su nizovi kovarijantni, pa je niz L ist< S tring> [] istovrem eno i
niz O bject]]. To možete upotrebiti za dodeljivanje liste A rrayL ist< Integer> svom nizu, a
da ne izazovete grešku ni u vreme prevođenja ni u vrem e izvršavanja.
Ukoliko znate da nećete svoditi naviše i potrebe su vam relativno jednostavne, lako ćete
napraviti niz generičkih tipova koji će biti elem entarno proveravan u vreme prevođenja.
M eđutim gotovo uvek je bolje izabrati generički kontejner nego niz generičkih tipova.
Po pravilu, videćete da su generički tipovi delotvorni na granicam a klase ili metode.
U njenoj unutrašnjosti, brisanje čini generički tip neupotrebljivim . Stoga ne možete, na
prim er, napraviti niz generičkog tipa:

/ / : nizovi/NizGenerickogTipa.java
// Java neće da prevede nizove generičkih 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 pokušali smo da napravim o nizove tipova
koji su bili obrisani i stoga su nepoznati. Im ajte u vidu da niz tipa Object m ožete napraviti
i eksplicitno konvertovati, ali bez anotacije @SuppressWarnings dobili biste upozorenje
,,unchecked“ u vreme prevođenja, zato što niz niti sadrži tip T niti se dinam ički proverava
na tip T. D rugim rečim a,ako napravite String[], Java će se i u vrem eprevođenja i u vreme
izvršavanja postarati da u taj niz m ožete sm eštati sam o objekte tipa String. M eđutim ,
ukoliko napravite niz Object[], u njega ćete m oći da sm eštate sve sem prostih tipova.
Vežba 8: (1) Dokažite tvrdnje iz preth od no g pasusa.
Vežba 9: (3) N apravite klase po trebn e za prim er ljusti<Banana> i pokažite da ih pre-
vodilac ne prihvata. Rešite problem tako što ćete napraviti o b ji' at tipa ArrayList.
Vežba 10: (2) Izm enite NizGenerickihTipova.java tako da se us. . sto nizova koriste kon-
tejneri. Pokažite da se tako gube i upozorenja u vrem e prevodenja.

Pravljenje podataka za testiranje


Prilikom eksperim entisanja s nizovim a i program im a uopšte, 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 prilično 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]
* ///:-

Možete da popunite ceo niz ili, kao što pokazuju poslednje naredbe, nekoliko eleme-
nata niza. M eđutim , pošto m etodom Arrays.fill() m ožete da popunite niz samo jednom
vrednošću, rezultati nisu naročito upotrebljivi.

Generatori podataka
Za pravljenje zanimljivijih nizova podataka, ali na fleksibilan način, upotrebićem o kon-
cept G en erato ra predstavljen u poglavlju Generički 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) - različiti G eneratori predstavljaju razli-


čite strategije1)-
U ovom odeljku napravićem o nekoliko Generatora; kao što ste već videli, lako ćete de-
finisati sopstvene.
Prvo, napravićem o osnovni skup generatora za brojanje svih om otača prostih tipova i
objekata tipa String. Klase generatora su ugnežđene u klasi GeneratorDateKoIicine da bi
mogle biti nazvane im enom tipa objekata koje generišu; 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 više liči 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 značenje pojm a kolićina. U slučaju 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. Veličinu niza određuje argu-
m ent konstruktora. O bratite pažnju na to da GeneratorDateKolicine.String upotreblja-
va elem entarni Generator<java.lang.Character> um esto specifične reference na
GeneratorDateKolicine.Character. Kasnije taj generator može zam eniti RandoinGene-
rator.String iz paketa RandomGenerator.java.
Poglav[je 16: Nizovi 609

Evo jedne alatke za testiranje koja upotrebljava refleksiju su agnežđenim Generato-


ro m , p a se m ože 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 sadrži skup ugnežđenih objekata tipa Ge-
nerator, od kojih svaki ima podrazumevani konstruktor (onaj bez argum enata). Sve ug-
nežđene 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 slučajnih brojeva. Pošto se kon-
stru k to r klase R andom inicijalizuje konstantom , rezultat se ponavlja svaki p u t kada po-
m oću jednog od ovih G eneratora pozovete program :

//: net/mindview/util/GeneratorSlucajnih.java
// Generatori koji daju slučajne 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
// Uključićemo generator slučajnih 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 uključuje Character generator.
Za generisanje brojeva koji nisu preveliki, G eneratorSlucajnih.Integer koristi stan-
dardn u vrednost m odula 10.000, ali prekjopljeni konstruktor om ogućuje i izbor m anje
vrednosti. Isti pristup je upotrebljen i za GeneratorSIucajnih.Long. Za Generatore Float
i Double, vrednosti iza decim alne tačke 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 određuje javno polje TestiranjeGeneratora.velicina koje


m ožete menjati.

Pravljenje nizova od Generatora


Za pravljenje niza od Generatora po treb ne su nam dve alatke za konverziju. Prva pom oću
bilo kojeg G eneratora pravi niz podtipova klase Object. Da bism o rešili problem prostih
tipova, druga alatka prim a proizvoljan niz om otača prostih tipova i proizvodi odgovara-
jući niz prostih tipova.
Prva alatka im a dve opcije koje predstavlja preklopljena statična m etoda array(). Prva
verzija m etode prim a postojeći 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 računa o tom e da ova alatka proizvodi sam o nizove
podtipova klase Object i da ne m ože da pravi nizove prostih tipova:

//: net/mindview/util/Generisani.java
package net.mindview.util;
import java.util

public class Generisani {


// Popunjavanje postojećeg 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 efiniđja 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 određuje drugi argum ent konstruktora. Svi podtipovi klase Col-
lection (kontejneri) imaju m etodu toA rray() koja elem entim a kontejnera popunjava niz
zađat argum entom .
Druga m etoda upotrebljava refleksiju za dinam ičko pravljenje novog niza odgovara-
jućeg tipa i veličine. On se zatim popunjava isto kao 11 prvoj m etodi.
Klasu G enerisani m ožemo 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 enerički tipovi ne rade s prostim tipovim a, a m i generatore hoćem o da upotrebim o
za popunjavanje nizova prostih tipova. Problem sm o rešili tak što sm o napravili konver-
tor koji p rim a proizvoljan niz om otačkih objekata i konvertuje ga u niz odgovarajućih
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 odgovarajući niz prostih tipova tačne dužine i /a
tim u njega kopira elemente niza om otačkih tipova ulaz. O bratite pažnju na to da se
autom atsko raspakivanje dešava u izrazu:

rezultat[i] = ul a z [ i ] ;

Evo prim era iz kojega možete 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 oću 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 obezbeđuje i da sve verzije m etode KonvertujU.prost() ispravno rade.
Vežba 11: (2) Pokažite da autom atsko raspakivanje ne radi s nizovima.
Vežba 12: (1) Napravite inicijalizovan niz tipa double pom oću klase GeneratorDateKo-
licine. Ispišite rezultate.
Vežba 13: (2) Popunite objekat tipa String pom oću klase GeneratorDateKolicine.Cha-
racter.
Vežba 14: (6) N apravite po jedan niz svakog od prostih tipova, zatim ih popunite pom oću
klase GeneratorDateKoIicine. Ispišite svaki niz.
Vežba 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().
Vežba 16: (3) Na osnovu program a GeneratorDateKolicine.java, napravite klasu
PreskociGenerator koja nove vrednosti proizvodi povećavanjem za 1, u skladu s argu-
m entom konstruktora. Prepravite program TestGenerisanjaNizova.java tako da pokaže
da vaša nova klasa radi ispravno.
Vežba 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 pronaći ćete klasu Arrays koja sadrži skup statičkih uslužnih funkcija
za nizove. Postoji šest osnovnih funkcija: equals(), za poredenje jednakosti dva niza (i de-
epEquals() za višedim enzionalne nizove), fill() za popunjavanje niza određenom vred-
nošću (već ste je upoznali u p retho d nom delu poglavlja), sort() za uredivanje elemenata
niza, binarySearch() za pronalaženje elem enta u uređenom nizu, toStringO za pravljenje
String objekta koji predstavlja niz, i hashCode(), za transform isanje ključa niza (saznaće-
te šta to znači 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, pogleđaćem o jednu korisnu m etodu
koja nije njen deo.
Poglavlje 16: Nizovi 617

Kopiranje niza
S tandarđna biblioteka u favi obezbeđuje statičku m etodu System.arraycopy() pom oću
koje se niz m nogo brže kopira nego kada se koristi petlja for za ručno 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 započeti kopiranje, odredišni niz, pom eraj u odredišnom nizu od koga treba da počne
kopiranje i broj elem enata za kopiranje. P rirodno, svako prekoračenje granica niza
prouzrokovaće izuzetak.
Prim er pokazuje da se m ogu kopirati i nizovi prostih tipova i nizovi objekata. M eđu-
tim , ako kopirate nizove objekata, kopiraju se sam o reference, ali ne i sami objekti. To se
zove površno 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 tačno istog tipa.
Vežba 18: (3) N apravite i popunite niz objekata tipa SferaOdBerilijuma. Kopirajte taj niz
u drugi, nov niz i pokažite da je to površna kopija.

Poređenje nizova
Klasa Arrays nudi preklopljene verzije m etode equals() za poređenje 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 odgovarajućem
elem entu drugog niza, pri čem u se m etoda equals () poziva za svaki element. (Za proste
tipove se koristi m etoda equals() om otačke klase, npr. Integer.equals() za tip int.) Evo
prim era:

//: nizovi/PoredjenjeNizova.java
// Korišćenje 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 početku, 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 slučaju, svi elem enti s l uka-
zuju na isti objekat, dok s2 im a pet jedinstvenih objekata. M edutim , jednakost nizova se
određuje prem a sadržaju (m etodom Object.equals()), pa je rezultat true.
Vežba 19: (2) N apravite klasu sa int poljem koje se inicijalizuje pom oću argum enta kon-
struktora. N apravite dva niza tih objekata koristeći identične inicijalizacione vrednosti za
oba niza i pokažite da m etoda Arrays.equals() kaže da oni nisu jednaki. Rešite problem
tako što ćete dodati m etodu equals() u svoju klasu.
Vežba 20: (4) Pokažite rad m etode deepEquals() za višedim enzionalne nizove.

Poređenje elemenata niza


Uređivanje se m ora izvoditi na osnovu stvarnog tipa objekta. Naravno, jedan p ristu p bio
bi da se piše posebna m etoda uređivanja za svaki tip podataka, ali verovatno već shvatate
da se tako ne dobija kod koji se m ože lako koristiti s različitim tipovima.
Najvažniji 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 opšti algoritam uređivanja, dok je ono što se m enja od
jedne do druge prim ene zapravo način poređenja objekata. Dakle, um esto ugrađivanja
koda za poređenje u različite potprogram e za uređivanje, koristi se projektni obrazac
Strategv.1 Strategija znači da se deo koda koji se razlikuje od slučaja do slučaja kapsulira
u n u tar posebne klase (objekta tipa Strategija). O bjekat tipa Strategija predajete delu koda
koji je uvek isti, a on pom oću te Strategije izvršava svoj algoritam. Na taj način se mogu
napraviti raziičiti objekti koji će izražavati različite načine poređenja i dostavljaće ih
istom kociu za uređivanje.
U Javi postoje dva načina za uređivanje. Prva je m etoda „prirodnog" poređenja koja se
ugrađuje 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 vraća negativnu vrednost ako je tekući objekat manji od argu-
m enta, nulu ako je tekući objekat jednak argum entu, odnosno pozitivnu vrednost ako je
tekući objekat veći od argum enta.
Evo klase koja realizuje interfejs C o m p arab le i ilustruje poređenje nizova pom oću
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 Desij’tt 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 uređivanja: ");
p r i n t ( Ar ra ys .t oS tr ing (a ));
Arrays.sort(a);
print("nakon uređivanja: ");
p r in t( Ar ra ys .t oS tr ing (a ));
}
} /* Ispis:
pre uređivanja:
[[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 uređivanja:
[[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]
]
* ///:-

Kađa definišete funkciju poređenja, sam i definišete šta znači uporediti jedan objekat
vaše 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() vraća objekat koji realizuje interfejt. Generator, tako što pravi
ano nim nu unutrašnju klasu. Na taj način, inicijalizacijom slučajnim 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 uređuje. D a interfejs Com parable nije
bio realizovan, pojavila bi se greška CIassCastException u vrem e prevođenja prilikom
pokušaja 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 sviđa kako on a radi i za određeni tip podataka radije bi-
ste koristili neku drugu funkciju za poređenje. D a biste to postigli, m orate da prim enite
drugi pristup za poređenje objekata. Treba da napravite posebnu klasu koja realizuje in-
terfejs C om parator (ukratko pređstavljen u poglavlju Č uvanje objekata). Ovo je p rim er
projektnog obrasca Strategy. C om parator im a dve m etode, compare() i equals(). M eđu-
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 nasleđuje iz klase Object koja im a svoju
m etodu equals(). Znači, m ožete da koristite stand ardn u m eto d u equals() klase Object i
da zadovoljite uslove koje nam eče interfejs.
Klasa Collections (njom e ćem o se pozabaviti u n arednom poglavlju) sadrži m etodu
reverseOreder(); m etoda pravi C om parator koji obrće p riro d an redosled uređivanja. To
se jednostavno prim enjuje na našu 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 uređivanja: ");
pri n t ( A rr ay s.toStri ng (a ));
)
} /* Ispis:
pre uređivanja:
[[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 uređivanja:
[ [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

Možete napisati i sopstveni Com parator. Sledeći 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 uređivanja: ");
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 uređivanja: ");
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 uređivanja:
[[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 ežba21: (3) P okušajteda uredite nizobjekata iz vežbe 18. Prim enite C o m p arab le da bi-
ste rešili problem . Potom napravite C o m p arato r za sortiranje objekata obrn u tim
redosledom.
Poglavlje 16: Nizovi 623

Uređivanje niza
Pom oću ugrađenih m etoda možete da uredite proizvoljan niz elem enata prostog tipa, od-
nosno bilo koji niz objekata koji realizuju C o m parab le ili im aju pridruženi C o m p ara-
to r.3 Evo prim era kojim se generišu, a potom i sortiraju, slučajni 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 uređivanja: ", 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 uređivanja: ", 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("Uređivanje 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 velićinu slova: " +
Arrays .t oS tr in g( sa ));
}
} /* Ispis:
Pre uređivanja: [YNzbr, nyGcF, 0WZnT, cQrGs, eGZMm, JMRoE, suEcU, OneOE,
dLsmw, HLGEa, hKcxr, EqUCB, bklna, Mesbt, WHkjU, rUkZP, gwsqP, zDyCy,
RFJQA, HxxHv]
Nakon uređivanja: [EqUCB, HLGEa, HxxHv, JMRoE, Mesbt, 0WZnT, OneOE,
RFJQA, WHkjU, YNzbr, bklna, cQrGs, dLsmw, eGZMm, gwsqP, hKcxr, nyGcF,
rUkZP, suEcU, zDyCy]
Uređivanje obrnutim redom: [zDyCy, suEcU, rUkZP, nyGcF, hKcxr, gwsqP,
eGZMm, dLsmw, cQrGs, bklna, YNzbr, WHkjU, RFJQA, OneOE, 0WZnT, Mesbt,
JMRoE, HxxHv, HLGEa, EqUCB]
Uređivanje bez obzira na veličinu 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 uređivanje niza elem enata
tipa S trin g leksikografski, odnosno na početak postavlja reči koje počinju velikim slovom,
a potom slede reči koje počinju malim slovom. (Ovako se obično uređuju stavke u tele-
fonskim im enicim a.) Ako hoćete da grupišete reči 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 ačuđo, u lavi 1.0 i 1.1 nije postojala podrška za uređivanje objekata tip a S trin g .
62 4 Misliti na Javi

Algoritam uređivanja 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 učinku, osim ako vam alatka za m erenje perfor-
m ansi ne ukaže na to da je postup ak uređivanja usko grlo.

Pretraživanje uređenog niza


Kada se niz uredi, određeni elem ent se m ože brzo potražiti pom oću m etode Arrays.bi-
narySearch(). M eđutim , veom a je važno da ne pokušate da prim enite m etodu
binarySearch() na niz koji nije uređen, 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 tražiti:

//: 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("Uređen 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 generišu se slučajne vrednosti za elem ente koji se traže, sve dok se jedna
vrednost ne pronađe.
M etoda A rrays.binaryS earch() vraća vrednost koja je veća od nule ili jednaka nuli ako
se pronađe traženi element. U sup ro tn om , vraća negativnu vrednost koja predstavlja me-
sto na koje bi taj elem ent trebalo da se u m etne kada bi se redosled niza ručno održavao.
Vraćena vrednost je:
Poglavlje 16: Nizovi 625

-(tačka umetanja) - 1

Tačka um etanja je indeks prvog elem enta koji je veći od traženog, odnosno a.size()
ako su svi elem enti niza m anji od traženog.
Ako niz sadrži više elem enata iste vrednosti, ne zna se koji će od njih biti pronađen. To
znači da algoritam ne podržava duplikate elem enata, već ih toleriše. Ako vam je potrebna
uređena lista bez ponovljenih elem enata, upotrebite klasu TreeSet (koja održava uređeni
redosled) ili LinkedHashSet (koja održava redosled um etanja). Ove klase se um esto vas
autom atski staraju o svim pojedinostim a. Samo u slučajevima kada vam je neophodno
poboljšanje perform ansi trebalo bi zam eniti, jednu od tih klasa nizom čiji se redosled
održava ručno.
Ako ste za uređivanje niza objekata koristili kom parator (nizovi prostih tipova se ne
m ogu uređivati p om oću kom paratora), m orate da upotrebite isti Com parator kada pre-
tražujete m etodom binarySearch() (pom oću preklopljene verzije ove funkcije). Na pri-
mer, program UređjivanjeTipaString.java m ože se prilagoditi tako da pretražuje niz:

//: nizovi/AbecednaPretraga.java
// Pretraživanje pomoću 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 treći argu-


m ent. U gornjem prim eru će elem ent koji se traži sigurno biti pronađen jer je dobijeu iz
sam og niza.
Vežba 22: (2) Pokažite da su rezultati prim ene m etode binaryS earch() na neuređen niz
nepredvidljivi.
Vežba 23: (2) N apravite niz objekata tipa Integer, popunite ga nasum ičnim in t vredno-
stim a (prim enite autom atsko pakovanje) i uredite ga obrn u tim redosledom pom oću
C om paratora.
Vežba 24: (3) Pokažite da se klasa iz vežbe 19 može pretraživati.
626 Misliti na Javi

Sažetak
U ovom poglavlju, videli ste da Java pruža pristojnu podršku za nizove neprom enljive ve-
ličine 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 veličine i niskog nivoa bili su
apsolutno neophodni, ne sam o zato što su projektanti Jave (i zbog perform ansi) odlučili
da Java im a i proste tipove, nego i zato što je podrška za kontejnere u toj verziji bila mi-
nim alna. Dakle, u ranim verzijama Jave, uvek je bilo preporučljivo koristiti nizove.
U kasnijim verzijama Jave, znatno se poboljšala podrška za kontejnere, pa o ni sada za-
senjuju nizove po svemu sem po perform ansam a, a i perform anse kontejnera su u m eđu-
vrem enu znatno poboljšane. Već sm o na više m esta rekli da problem e s perform ansam a
obično ne prouzrokuje ono na šta program er sum nja.
Nakon uvođenja autom atskog pakovanja i generičkih tipova, držanje prostih tipova u
kontejnerim a postalo je lako, pa je i to razlog da nizove zam enite kontejnerim a. Nizovi
više nem aju ni tu isključivu prednost da om ogućuju bezbedan rad s tipovim a, pošto i ge-
nerički tipovi daju kontejnere čiji se tipovi autom atski proveravaju.
Rekosmo u ovom poglavlju da su generički tipovi prilično nepodesni za rad s nizovi-
ma, a i sami ćete se uveriti kada pokušate da ih upotrebite. Često se dešava da u vreme
prevođenja dobijate upozorenja unchecked čak i kada postignete da generički tipovi i ni-
zovi na neki način sarađuju (kao što ćete videti u n arednom poglavlju).
Kada se raspravljalo o konkretnim prim erim a, projektanti jezika Java u više navrata su
m i govorili da bi umesto nizova trebalo da koristim kontejnere (pom oću nizova sam po-
kazivao specifične tehnike, pa tu m ogućnost 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 dokaže 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 pređete na nizove.
Ovo je prilično hrabra tvrdnja, ali neki jezici uopšte nem aju nizove niskog nivoa i ne-
promenljive veličine. Imaju samo kontejnere prom enljive veličine koji su m nogo funk-
cionalniji od Javinih nizova. Prim era radi, P ython4 im a tip list čija sintaksa liči na onu
elem entarnih nizova, ali mnogo funk đon alniji - m ože 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) # Veličina 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 ): # Nasleđivanje liste

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

# Definicija metode, pokazivač 'this' se piše eksplicitno:


def d a jO br nu tu (s el f):
obrnuta = s e l f [:] # Kopiranje liste pomoću delova
obrnuta.reverse() # Ugrađena 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


navodeći u n u tar uglastih zagrada niz objekata radvojenih zarezima. Rezultat je objekat
tipa list u vrem e izvršavanja (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 postižem o „deljenjem", tako što operator : sm estim o u n u tar
operacije indeksiranja. Tip list im a još m nogo ugrađenih operacija.
MojaLista je definicija klase; osnovne klase se pišu u zagradam a. U nutar klase, nared-
be def proizvode m etode, a prvi argum ent m etode autom atski postaje ekvivalentan rezer-
visanoj reči 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 pažnju na auto-
m atsko nasieđivanje konstruktora.
Iako u P ythonu zapravo postoje sam o objekti (m eđu kojim a i tipovi za cele brojeve i
brojeve s pokretnim zarezom ), izlaz u nuždi za optim izovanje perform ansi kritičnih delo-
va program a predstavlja pisanje dodataka na jezicim a C, C + + ili pom oću Pyrexa, speci-
jalne alatke koja olakšava ubrzavanje koda. Na taj način možete ostvariti i čistoću
objekata i poboljšane 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 počinju iz početka. 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 višeg nivoa kao što su kontejneri. D odajte tom e sledeću činjenicu: ukoliko
su kontejneri ugrađeni 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 ožem o pobeći; nailazićete na njih u kodu koji čitate.
M eđutim , kontejneri su gotovo uvek bolje rešenje.
Vežba25: (3) Napišite P ythonoveL iste.py u Javi.
R ešenja o d a b r a n ih vežbi 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 početak rada s njim a. U ovom poglavlju iserpnije ćemo
istražiti 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


više nego što sadrži poglavlje Č uvanje objekata. Pošto je ovo poglavlje zasnovano na na-
prednijem gradivu (kao što su generički tipovi), ođložili sm o ga do ovog m esta u knjizi.
N akon što dam o potpuniji pregled kontejnera, saznaćete kako radi transform isanje
ključa (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 ključeva. Naučićete zašto postoje različite verzije nekih konte-
jnera i kada koji treba upotrebiti. Poglavlje ćemo završiti razm atranjem uslužnih m etoda
opšte nam ene i specijalnih klasa.

Potpuna taksonomija kontejnera


U ,,Sažetku“ 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 sledeće:


• 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 izvršavatije.
• Interfejs ConcurrentM ap i njegova realizacija ConcurrentHashM ap, takođe za
u potrebu u višenitnom radu i to je prikazano u poglavlju Paralelno izvršavanje.
• CopyOnWriteArrayList i CopyOnWriteArraySet, takođe za paralelno izvršavanje.
• 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.
• Više uslužnih m etoda u klasi Collections.
Pravougaonici oivićeni isprekidano (ali ne najkraćim crticam a) predstavljaju apstrakt-
ne klase. Vidi se da im ena više klasa počinju sa Abstract. Isprva to može da zbuni, ali radi
se o alatkam a koje delimično realizuju određeni interfejs. D a pravite sopstveni skup, na
prim er, ne biste počeli 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 eđutim , biblioteka kontejnera sadrži gotovo svu funkcionalnost koja vam ikada m ože
zatrebati, pa najčešće možete zanem ariti sve klase čija im ena počinju sa Abstract.

Popunjavanje kontejnera
M ada je problem sa štam panjem sadržaja kontejnera rešen, popunjavanje kontejnera ima
isti neđostatak kao java.util.Arrays. Kao u paketu Arrays, postoji prateća klasa nazvana
Collections koja sadrži statične uslužne m etode, m eđu 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 može
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 načina popunjavanja kontejnera (objekta tipa Collec-
tion) referencama na jedan objekat. Prvo, m etoda CoUections.nCopies( ) pravi listu koja
se prosleđuje 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 heširanje (engl. hash code) tog objekta - ge-
nerisan m etodom h ashC ode( ). Iz rezultata vidite da sve reference upućuju na isti obje-
kat, a isto važi i nakon poziva druge m etode, Collections.fill( ). M etodu fill( ) još m anje
korisnom čini to što može da zam eni sam o elem ente koji su već u listi - ona ne može da
dodaje nove elemente.

Generatorsko rešenje
Gotovo svi podtipovi interfejsa Collection imaju konstruktor koji prim a drugi objekat
tipa Collection, iz kojega može da popuni nov kontejner. Dakle, da bism o sačinili 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 Generički tipovi i pobliže objašenjeni 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 () );
}
// Generička pomoćna 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


može proslediti konstruktoru bilo kojeg drugog kontejnera koji će podatke kopirati u se-
be. Za popunjavanje postojećeg kontejnera može se upotrebiti i m etoda addAH( ) koju
im aju svi podtipovi klase Collection.
Poglavlje 17: Detaljno razmatranje kontejnera 631

Kada se koristi pom oćna generička m etoda, sm anjuje se količina koda potrebnog za
u p otreb u klase.
Program PodaciKontejnera je prim er projektnog obrasca A da pter (Adapter);' on
prilagođava 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));
// Korišćenje pomoćne 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, pošto L inkedH ashSet održava ulančanu
listu koja zadržava redosled umetanja.
Svi generatori definisani u poglavlju N izovi sada su dostupni preko adaptera Podaci-
K outejnera. U ovom prim eru upotrebićem o dva od njih:

//: ko ntejneri/GenerisanjePodatakaKontejnera.java
// Korišćenje 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 ( // Pomoćna 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 užinu objekta tipa String koji pravi R andom G enerator.String određuje konstruktor
u argum entu.

Generatori mapa
Na isti način m ožem o napraviti m apu, ali za nju nam treba klasa Par, pošto 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 ože 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);
}
}
// Generičke pomoćne 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 pruža izbor - može 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 oćne generičke m etode sm anjuju količinu koda po-
treb n u za pravljenje objekta tipa PodacilzMape.
Evo prim era korišćenja program a PodacilzMape. I G enerator klase Slova realizuje in-
terfejs Iterable pravljenjem Iteratora; zato se m ože 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 ključa 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 možete da generišete proizvoljan skup podataka za Mape i kontejnere
(objekte tipa Collection) i zatim da ih iniđjalizujete konstruktorom ili metodama
M ap.putA lI() odnosno Collection.addAlI().

Korišćenje 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 ično
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 uobičajeno u
slučaju podataka za testiranje, broj m etoda koje m orate da napravite je m inim alan.
Iako to nije bilo neophodno, sledeće rešenje nam je dalo priliku da prikažem o još je-
dan projektni obrazac Flyweight (Zam ajac). F lyw eight se koristi kada je za uobičajeno
rešenje neophodno previše objekata ili kada pravljenje norm alnih objekata zauzima pre-
više prostora. Projektni obrazac F lyw cight eksternalizuje deo objekta tako što, um esto da
se ceo objekat drži u sam om objektu, deo objekta ili ceo objekat traži se u efikasnijoj,
spoljnoj tabeli (ili pravi nekim drugim p ro račun om koji štedi prostor).
Važno je da uočite kako se nam enski objekti tipa Map i Collection Iako prave nasleđi-
vanjem klasa java.util.Abstract. Da biste napravili objekat tipa Map sam o za čitanje, na-
sleđujete klasu AbstractMap i realizujete m etodu entrySet( ). D abiste napravili objekat tipa
Set samo za čitanje, nasleđujete klasu AbstractSet i realizujete m etode ite ra to r( ) i size( ).
Skup podataka u ovom prim eru jeste m apa država i njihovih glavnih gradova.2 Meto-
da glavni_gradovi( ) pravi m apu država i glavnih gradova. M etoda im e n a ( ) pravi listu
engleskih imena država. U oba slučaja dobićete delim ični listing ukoliko željenu veličinu
naznačite 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 S’V ' P o r t Louis"}, {"M 0R 0C C0 ","R ab at "},
{"MOZAMBIQ UE ", "M ap uto "}, {"NAMIBIA","Windhoek"},
{" N I G E R " ,"Ni a m e y " } , {"N IG ER IA ","A bu ja "} ,
{"R W A N D A " ,"Kigali" },
{"SAO TOME E P R I N CI PE ","Sao Tome"},
{"S E N E G A L " ,"D akar"} , {"S EY CH EL LE S","Victoria " } ,
{"SIERRA L E ON E" ," Fr ee to wn "}, {" S O M AL IA ","M og adishu"} ,
{"SOUTH A F R I C A " ,"Pretoria/Cape Town"},
{"SUDA N' V' Kh ar to um "},
{"S WA Z I L A N D " ,“M b a b a n e " }, {"T AN Z A N I A " , "Dodoma'1} ,
{"T O G O ","Lo me“ } , {“T U N I S I A " ," T un is“ },
{" U G A N D A " ,“Kampal a " } ,
{"DEMOCRATIC REPUBLIC OF THE CONGO (Z AIRE)","Kinshasa"},
{" Z A M B I A " ,"Lusaka"} , {"Z IM B A B W E " ," H ar ar e"} ,
// Azija
{"A FG HA NI ST AN","Kabu 1 " }, {" B A H RA IN ", "Manama''},
{" BA NG LA DE SH ", "D ha ka" }, { " B HU TA N","Thimp hu "},
{"BRUNEI","Bandar Seri Begawan"},
{"C AM B O D I A " ,"Phnom Penh"},
{"CHINA","Beijing"}, {"CYPRUS","Nicosia"},
{" I 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 " } ,
// Istočna Evropa i bivši 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},
// Južna 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 "},
};
// Realizovaćemo 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 metođa 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 veća 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 delimične mape država, date veličine:
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); // Delimična mapa
}
static List<String> imena =
// Sva imena:
public static List<String> imena() { return imena; }
// Delimična 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 može 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 skladišti sam o svoj indeks, a ne stvarni ključ i vrednost. Kada po-
zovete m etodu getKey( ) ili getV alue( ), ona će vam vratiti odgovarajući elem ent PODA-
CI određen pom oću tog indeksa. Klasa SkupStavki se stara da njena velicina ne bude
veća 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
sadrži sam o indeks statičnog niza znakovnih nizova. Svaki p u t kada pozovete m etodu
n e x t( ) za taj iterator, indeks u objektu Stavka povećava se za 1, tako da pokazuje na sledeći
par elemenata, i potom se jedini objekat tipa Stavka tog Iteratora vraća iz m etode next( ).3
M etoda select( ) pravi objekat tipa FlyweightMapa koji sadrži SkupStavki željene veliči-
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, ograničena veličina klase C ountries predstavlja problem . Na isti
način m ožem o napraviti inicijalizovane nam enske kontejnere koji im aju skup podataka
proizvoljne veličine. Sledeća klasa je lista proizvoljne veličine koju (zapravo) unapred ini-
cijalizujemo Integer podacim a:

//: net/mindview/uti1/BrojackaListaIntegera.java
// Lista proizvoljne dužine 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 rešenje sa zamajcem: g e t( ) pravi vrednost kada
je zatražite, 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 sadrži unapred inicijalizovane jedinstvene objekte tipa In teg er i
String; i ona može biti proizvoljne veličine:

//: net/mindview/util/BrojackaMapaPodataka.java
// Mapa neograničene dužine 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 zadržava 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.
Vežba 1: (1) Napravite listu (pokušajte da sačinite i ArrayList i LinkedList) i popunite je
pom oču klase Countries. Listu uredite i ispišite, zatim na nju više p u ta prim enite
CoIlections.shuffle( ) i svaki p u t je ispišite, da biste videli kako m etoda shuffle( ) svaki
p u t drugačije ispremeša listu.
Vežba 2: (2) Napravite m apu i skup koji sadrže im ena svih država koja počinju na A.
Vežba 3: (1) Pom oču klase Countries, više p u ta popunite neki objekat tipa Set istim
podacim a i đokažite da dobijeni skup (Set) im a sam o po jedan prim erak svakog objekta.
Isprobajte to s klasama HashSet, LinkedHashSet i TreeSet.
Vežba 4: (2) Napravite inicijalizator kolekcije koji otvara datoteku i deli je na reči m eto-
dom TextFile( ) (interfejsa Collection), a zatim te reči koristi kao izvor podataka za do-
bijeni kontejner. Pokažite da to radi.
Vežba 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 možete da uradite s kolekcijama (nisu obuhvaćene
m etode koje se autom atski nasleđuju iz klase Object), što znači 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 generičkog tipa T bude u kontejneru. Vraća


false ako ne doda argument. (Ovo je opciona metoda, opisana u
sledećem odeljku.)
boolean addAllf Dodaje kolekciji sve elemente iz argumenta. Vraća true ako je dodat
Kolekcija<? extends T>) neki element. (Opciona metoda.)
void clear( ) Uklanja sve elemente iz kontejnera. (Opciona metoda.)
boolean contains(T) Vraća true ako kontejner sadrži argument.
boolean containsAII( Vraća true ako kontejner sadrži sve elemente iz argumenta.
Kolekcija<?>)
boolean isEmpty( ) Vraća true ako u kontejneru nema elemenata.
64 4 Misliti na Javi

lterator<T> ite rato rf) Vraća lterator<T> koji se može koristiti za kretanje kroz elemente kon-
tejnera.
boolean remove(Object) Ako se argument nalazi u kontejneru, uklanja sejedna instanca tog ele-
menta. Vraća true akoje nešto uklonjeno. (Opciona metoda).
boolean removeAII) Uklanja sve elemente koje sadrži argument. Vraća true ako je nešto
Kolekcija<?>) uklonjeno. (Opciona metoda).
boolean retainAllf Zadržava samo elemente koji se nalaze u argumentu (skupovni presek).
Kolekcija<?>) Vraća true ako se nešto promenilo. (Opciona metoda).
int size( J Vraća broj elemenata u kontejneru.
ObjectfJ to A rra y () Vraća niz koji sadrži sve elemente kontejnera.
<T> T[] toArray(T[J a) Vraća niz koji sadrži sve elemente kontejnera čijije tip isti kao tip niza a,
a ne prosto Object. (Niz se mora konvertovati u odgovarajući tip).

Im ajte u vidu da ne postoji m etoda g e t( ) za izbor elem enta nasum ičnim pristupom .
Razlog je to što interfejs Collection sadrži i Set koji održava svoj unutrašnji poredak (pa
bi pretraživanje nasum ičnim 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 zajednički imenitelj" upotrebljen je ArrayList.

//: kontejneri/MetodeKolekcija.java
// Šta sve možete 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]);
// Pronalaženje najvećeg i najmanjeg elementa;
// to podrazumeva različite metode, u zavisnosti
// od načina 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);
// Zadržavanje 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 sadrže različite skupove podataka i bivaju svedene naviše na
Collection objekte, pa je jasno da se sem interfejsa Collection ne upotrebljava ništa drugo.
U m etodi m a in ( ), pom oču jednostavnih vežbi prikazane su sve m etode klase Collection.
N aredni odeljci u ovom poglavlju opisuju različite realizacije interfejsa List, Set i Map.
Zvezdicom (*) je uvek označena 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, sretaćete se s njim a u starom kodu.

Opcione operacije
M etode koje obavljaju razne vrste dodavanja i uklanjanja jesu opcione operacije interfejsa
Collection. To znači da klasa koja realizuje interfejs ne m ora da sadrži funkcionalne de-
finicije tih m etoda.
To je veom a neobičan način definisanja interfejsa. Kao što ste videli, u objektno ori-
jentisanom program iranju interfejs je vrsta ugovora. Pom oću njega se kaže: „Bez obzira
na to kako odlučite da realizujete ovaj interfejs, jem čim da ovom objektu m ožete da šalje-
te ove poruke.“4 Ali opciona operacija krši to tem eljno obećanje: ne sam o da pozivanje
ovih m etoda neće uraditi ništa korisno, već će i izazvati izuzetak! Kao da sm o izgubili bez-
bednost tipova u vreme prevođenja.
Nije sve baš tako strašno. U kolekciji, listi, skupu ili m api, prevodilac vas još uvek ogra-
ničava na pozivanje isključivo onih m etoda koje se nalaze u tom interfejsu. To je ipak
bolje od dinam ičkih jezika koji pružaju m ogućnost pozivanja svake m etode za svaki ob-
jekat, da bi se tek pri pokretanju program a ustanovilo da li pozvana m etoda uopšte može
da radi5. O sim toga, m noge m etode čiji je argum ent kolekcija, isključivo čitaju tu kolek-
ciju: nijedna m etoda za čitanje kolekcije nije opciona.
Zašto bi iko zadao da je m etoda opciona? Ovakav pristup sprečava eksploziju broja in-
terfejsa. Drugačiji načini projektovanja biblioteke kontejnera uvek se završe sa nerazm r-
sivom gom ilom interfejsa koji opisuju sve varijacije glavne tem e i zbog toga ih je teško
savladati. Nije čak ni m oguće obuhvatiti sve specijalne slučajeve u obliku interfejsa, pošto
uvek m ogu da se izmisle novi interfejsi. P ristupom „operacija koje nisu podržane“ postiže
se važna svrha (avine biblioteke kontejnera: kontejneri se lako savladavaju i koriste; ope-
racije koje nisu podržane specijalan su slučaj koji se može naučiti i kasnije. M eđutim , da
bi takav pristup delovao, p otrebno je sledeće:
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 većini
ldasa trebalo bi da rade sve operacije, a sam o u specijalnim slučajevima operacija ne
bi trebalo da bude podržana. To važi u Javinoj biblioteci kontejnera pošto klase koje
najčešće koristite (A rrayList, LinkedList, H ashSet i H ashM ap), kao i druge kon
kretne realizacije, podržavaju sve operacije. Ovakav pristup om ogućuje da napravi-
te novu kolekciju, da pri tom ne obezbedite definicije svih m etoda u interfejsu
C ollection, a da je ipak uklopite u postojeću biblioteku.

4 Terrnin ,,interfejs“ je ovde u p o treb ljen tako da obuhvata i fo rm alnu rezervisanu reč in terface i opštije
značenje: „m eto đ e p o d ržan e u svim klasam a i potklasam a".
5 M ada ovo zvuči ču d n o i m ožda beskorisno kada je o p isan o na taj način, videli ste, naročito u pogla-
vlju Podaci o tipu, da takvo d inam ičko p on ašanje m ože biti veom a m očno.
Poglavlje 17: Detaljno razmatranje kontejnera 647

2. Ako operacija nije podržana, obično postoji velika verovatnoća 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 grešku: korišćenje
realizacije na nepravilan način.
Im ajte u vidu da se nepodržane operacije m ogu otkriti tek u vreme izvršavanja, te sto-
ga predstavljaju dinam ičku proveru tipova. Ako ste koristili jezik sa statičnom proverom
tipova kao što je C + +, m ožda vam i Java izgleda kao jezik u kojem se tipovi proveravaju
statično. Java svakako im a statičnu proveru tipova, ali i znatnu količinu dinam ičke pro-
vere tipova, pa je teško reći da je tačno jednog ili drugog tipa. Kada postanete svesni toga,
počećete da uočavate i druge slučajeve dinam ičke provere tipova u Javi.

Nepodržane operacije
U Javi su čest izvor nepodržanih operacija kontejner i pripadna struktura podataka ne-
prom enljive dužine. Takav kontejner (u gornjem prim eru, listu) od niza proizvodi m eto-
da A rrays.asList( ). S druge strane, vi odlučujeteda li će bilo koji kontejner (uključujući tu
i m ape) generisati izuzetke U nsupportedO perationException zbog upotrebe nepro-
menljivih m etoda u Idasi Collections. P rim er koji sledi sadrži oba slučaja:

//: kontejneri/Nepodrzane.java
// Nepodržane 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 veličinu 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
* ///:-

Pošto m etoda A rrays.asList( ) pravi listu od niza odredene dužine, ima smisla da
bud u podržane 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 nepodržane m etode (grešku program era).
Vodite računa o tom e da svakoj kolekđji uvek možete da prosledite rezultat m etode
Arrays.asList( ) kao konstruktorski argum ent (ili da pozovete m etodu addA lI( ), ili sta-
tičnu m etodu C ollections.addA ll( )) da biste napravili pravi kontejner koji om ogućuje
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 dužine.
Nepromenljive m etode klase Collections omotavaju kontejner posrednikom koji izaziva
UnsupportedOperationF.xception ako izvršite bilo koju operaciju koja na bilo koji način
menja taj kontejner. Cilj korišćenja tih m etoda jeste pravljenje konstantnog kontejnerskog
objekta. Navešćemo 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 dođe granularnost tehnike nepodržana operacija -
dobijeni interfejs m ože da se razlikuje za je d n u m etodu izm eđu objekta koji vraća
A rrays.asList( ) i objekta koji vraća m etoda Collections.nepromenljivaLista( ).
A rrays.asList( ) vraća listu fiksne dužine, dok C ollections.neprom enljivaLista( ) proiz-
vodi listu koja se ne m ože menjati. Kao što vidite iz rezultata, m ožete m o difikovati ele-
m ente liste koju vraća m etoda A rrays.asList( ), jer to ne bi narušilo neprom enljivu
dužinu te liste. S druge strane, jasno je da rezultat m etode unm odifiableList( ) ne bi tre-
balo da bude prom enljiv ni na koji način. 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.
Vežba 6: (2) O bratite pažnju na to da klasa List im a dodatne opcione operacije koje Col-
lection ne obuhvata. Napišite verziju program a Nepodrzane.java za testiranje tih dodat-
nih opcionih operacija.

Funkcije liste
Kao što ste videli, prost tip List se prilično lako koristi. Iako najćešće 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 sleđi obavlja drugu grupu aktivnosti: ono što svaka li-
sta m ože da uradi (osnovniTest( )), kretanje po listi pom oću iteratora
(k retanjeIteratoroin( )) u odnosu na izm enu objekata pom oću iteratora (prom enalte-
ratorom f )), prikaz dejstva obrade listi (vidljivTest( )), i operacije dostupne sam o u
ulanćanim listam a (LinkedList):

//: kontejneri/Liste.java
// Šta se sve može 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 počev 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 omogućuju nasumičan pristup, što je brzo
// kod ArrayList, sporo kod LinkedList:
s = a.get(l); // Uzmi objekat (određenog 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 (); // Običan Iterator
lit = a. li st It er at or (); // Listlterator
lit = a . l i st It er at or (3 ); // Počni 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"
// Zadrži 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) );
// Obriši 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(); // Obriši 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( );
// Obriši 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
// pomoću 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 ulančane 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,
// izvlačenje 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-


govarajuća sintaksa, a njihova p o v ratna vrednost se nigde ne koristi, iako se pam ti. U ne-
kim slučajevima, povratna vrednost se ne p am ti jer se obično ne koristi. Pre nego što
upotrebite ove funkcije, proučite sve načine njihovog korišćenja u dokum entaciji na Web
adresi java.sun.com .
Vežba 7: (4) N apravite A rrayL ist i L inkedList i popunite obe generatorom
C o u n trie s.iin e n a (). Ispišite svaku Iistu pom oću običnog iteratora, zatim L istlteratorom
um etnite jednu listu na svaku dru gu lokaciju druge liste. Potom obavite um etanje počev
od kraja prve Iiste unazad.
Vežba 8: (7) N apravite generičku klasu jednostruko ulančane liste i nazovite je SList.
Neka ona ne realizuje interfejs List, tako je jednostavnije. Svaki Link objekat u listi treba
da sadrži referencu na sledeći elem ent liste, ali ne na prethodni (za razliku od nje, Linked-
List je dvostruko ulančana lista, što znači da održava 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
način um etanja i uklanjanja elem enata iz objekta tipa SList om ogućuje SListlterator. Na-
pišite kod koji pokazuje rad liste Slist.

Skupovi i redosled skladištenja


Prim eri skupova u poglavlju Č uvanje objckata predstavljaju d obar uvod u operacije koje
se m ogu obaviti sa osnovnim skupovim a. M eđutim , u tim prim erim a bili su prigodno
korišćeni 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 održavanje redosleda sm eštanja elem enata. Način održavanja redosleda
sm eštanja m enja se u zavisnosti od realizacije interfejsa Set. Dakle, različite realizacije in-
terfejsa Set ne sam o da imaju različita ponašanja, nego su različiti i zahtevi u pogledu tipa
objekta koji se može staviti u određeni 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 definišu metodu eq u a ls[J kojom se utvrđuje jedinstvenost
objekta. Set ima istovetan interfejs kao Collettion. Interfejs skupa ne
garantuje održavanje elemenata u određenom rasporedu.
HashSet* Klasa koja se koristi za skupove gde je važno brzo pronalaženje ele-
menata. Objekti moraju da definišu i metodu hash C o d ef).
TreeSet Ureden skup u obliku stabla. Iz ovog skupa mo/cte da izdvojite niz
ureden u određenom redosledu. Elementi moraju da definišu i interfejs
Comparable.
LinkedHashSet Ima brzinu pretraživanja klase HashSet. ali iraerno održava redosled
kojim su elementi bili umetani (redosled umetania) pomoću ulančane
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 sledeće: ako nem a dri,;>ih ograničenja, to treba da
bude podrazum evani izbor zato što je optim alan po brzini.
O brazac za definiciju m etode hashC od e( ) biće opisan u instavku poglavlja. eq u als( )
m orate da definišete i za skladištenje s transform acijom kljvič u (heširanje) i za skladištenje
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, pošto 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
nalaže da se uvek redefiniše m etoda h ashC o de( ) ako se redeimiše eq u als( ).
Ovaj prim er ilustruje m etode koje m oraju biti definisane i ; bi se određeni tip uspešno
upotrebljavao sa određenom 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); // Pokušaj 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 određeni skup i da bi se istovrem eno
izbeglo dupliranje koda, napravljene su tri klase. O snovna klasa TipSkupa prosto skla-
dišiti jedan int i ispisuje ga m etodom to S trin g ( ). Pošto sve klase uskladištene u skupovi-
ma m oraju im ati m etodu eq u als( ), i ona je uskladištena u osnovnoj klasi. Jednakost je
zasnovana na vrednosti celog broja i.
TipHesiranja nasleđuje TipSkupa i dodaje m etodu h ashC ode( ) neophodnu da bi ob-
jekat bio smešten u realizaciju interfejsa Set s transform isanim ključem.
T ipS tabla realizuje interfejs C om p arable koji je neophodan ako će objekat biti ko-
rišćen u uređenom kontejneru, kao što je SortedSet. (Jedina trenutno dostupna klasa
koja ga realizuje je TreeSet.) O bratite pažnju na to da u m etodi co m p areT o () nisam ko-
ristio jednostavnu i očiglednu konstrukciju r e tu rn i-i2. Iako je to česta greška u pro-
gram iranju, sve bi radilo kako treba sam o kada bi i i i2 bili neoznačeni celi brojevi (pod
uslovom da Java im a rezervisanu reč u n sig n ed za neoznačen ceo broj, što nije siučaj).
Greška se pojavljuje za označen ceo broj koji nije dovoljno velik da predstavi razliku dva
označena cela broja (tip int). Ako je i veliki pozitivan ceo broj, a j veliki negativan ceo
broj, i-j će dovesti do prekoračenja i daće negativnu vrednost, što nije ispravno.
O d m etode co in p areT o () obično očekujete da proizvodi prirodan redosled usaglašen
s m etodom e q u a ls ( ). Ako e q u a ls () daje tru e za određeno poređenje, onda bi
co m p areT o () trebalo da da nulu kao rezultat istog poređenja, a ukoliko e q u a ls () daje fal-
se za neko poređenje, onda bi co m p areT o () za isto poređenje trebalo da da rezultat raz-
ličit od nule.
U klasi TipoviZaSkupove, m etode f ill( ) i te s t ( ) definisane su pom oću generičkih
tipova da bi se sprečilo dupliranje koda. Ponašanje skupa (objekta tipa Set) m etoda te s t( )
proverava s tri poziva m etode fill() za ispitni skup, pri čem u pokušava da u njega ubaci
duplikate objekata. Metoda f ill() prim a skup proizvoljnog tipa i Class objekat istog tipa.
Pomoću 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 uređenom poretku (zbog načina realizacije me-
tode co m p areT o (), to je slučajno opadajući poredak).
656 Misliti na Javi

Ukoliko pokušam o da upotrebim o tipove koji ne podržavaju ispravno operacije sa sku-


povim a koji te operacije zahtevaju, sve se ruši. Smeštanje objekta tipa TipSkupa ili Tip-
Stabla koji ne obuhvata redefinisanu m etodu hashC ode( ) u bilo koju realizaciju s
transform isanjem ključa rezultuje dupliranjem vrednosti, čime se krši osnovno pravilo
skupova (interfejsa Set). To prilično obespokojava, pošto se greška ne generiše čak ni to-
kom izvršavanja. M eđutim, podrazum evana m etoda hashC ode( ) legitim na je, pa je takvo
ponašanje dozvoljeno, uprkos tom e što je nekorektno. Jedini pouzdan način da se obezbedi
ispravnost takvog program a jeste uključivanje testiranja anotacijam a &unit u build sistem
(više inform acija potražite u dodatku na adresi http://M indV iew .net/B ooks/B etterJava).
Ako u objektu tipa TreeSet pokušate da upotrebite tip koji ne realizuje interfejs Com-
parable, dobićete određeniji rezultat: kada TreeSet pokuša da upotrebi taj objekat kao
uređen (jer m etoda com pareTo( ) definiše neki poredak), biće generisan izuzetak.

SortedSet
Elem enti u kontejneru tipa SortedSet zajemćeno su u uređenom poretku, zbog čega sle-
deće m etode u interfejsu SortedSet m ogu da pruže 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 najniži element.
Object Ia st( ): Proizvodi najviši element.
SortedSet subSet(odElement, doElement): Proizvodi prikaz ovog skupa sa elemen-
tim a od odElement, uključivo, do doEIement, isključivo.
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 većim 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 može 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 znači „uređeno u skladu s funkcijom poređenja objekta“, a


ne „uređeno po redosledu um etanja“. Redosled um etanja se m ože očuvati pom oću kon-
tejnera LinkedHashSet.
Vežba 9: (2) U potrebite Ranđom Generator.String za popunjavanje objekta tipa TreeSet,
ali uz abecedni redosled. Ispišite TreeSet da biste proverili redosled sortiranja.
Vežba 10: (7) Definišite sopstveni SortedSet pom oću objekta tipa LinkedList kao pripad-
ne realizacije.

Redovi za čekanje
Seni u aplikacijama za paralelno izvršavanje, Java SE5 je realizovala Queue (red za čekanje)
samo u kontejnerim a LinkedList i PriorityQueue koji se razlikuju po ponašanju, a ne po
perform ansam a. Sledi elem entarni prim er s večinom realizacija reda za čekanje (koje u
ovom prim eru neće sve funkcionisati), uključujući tu i redove za čekanje napravljene za
paralelno izvršavanje. Elemente treba um etati od jednog kraja, a vaditi od drugog:

//: kontejneri/PonasanjeRedaZaCekanje.java
// Poredi ponašanje 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 objašnjenje redova za čekanje s prioritetom naći ćete u poglavlju Č uvanje objc-
kata. Zanimljiviji problem je lista zadataka, gde svaki objekat sadrži 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
// Složenija 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 smeće", 'C', 4);
ListaZadataka.add("Nahraniti psa", 'A', 2);
List aZ ad a t a k a . a d d ("Nahraniti ptičicu", '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 mačku", '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 mačku
B7: Nahraniti ptičicu
C3: Pokositi travu
C4: Baciti smeće
* ///:-

O bratite pažnju na to da su u redu za čekanje s prioritetom stavke sortirane


autom atski.
660 Misliti na Javi

Vežba 11: (2) Napravite klasu koja sadrži objekat tipa Integer inicijalizovan m etodom ja-
va.util.Random na vrednost izm edu 0 i 100. Realizujte Comparable pom oću tog Integer
polja. Popunite objekat tipa PriorityQueue objektim a vaše klase i izvucite te vrednosti
m etodom p o I l( ) da bi se pokazalo da kontejner proizvodi očekivani poredak.

Dvostrani redovi za čekanje


D vored (dvostrani red za čekanje, engl. double-ended queue, dequeue) jeste red za čekanje u
koji elemente možete dodavati i iz njega ih uklanjati sa oba kraja. Kontejner LinkedList
im a i m etode koje podržavaju operacije u dvostranim redovim a za čekanje, ali standardne
Javine biblioteke ne sadrže eksplicitan interfejs za njih. Zato LinkedList ne može da reali-
zuje taj interfejs i nije moguće svođenje naviše na interfejs Deque, dok je u prethodnom
prim eru bilo moguće svođenje na Queue. M eđutim , klasu tipa D eque m ožete napraviti
pom oću 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 ređe umeću na oba kraja i vade sa oba kraja, pa se Dvored koristi rede od
običnog 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 održava parove ključ-vrednost (asocijacije, pridruživanja, m apiranja, presli-
kavanja), tako da vrednost m ožete pronači pom oću ključa. Standardna Javina biblioteka
sadrži različite osnovne realizacije Mape: HashMap, TreeMap, LinkedHashMap, Weak-
HashMap, ConcurrentHashMap i IdentityHashMap. Svima im je zajednički osnovni
interfejs Map, a razlikuju se u ponašanju i efikasnosti, poretku čuvanja i prezentovanja
parova, roku čuvanja objekata u mapi, radu m ape u višenitnim program im a i načinu
utvrđivanja jednakosti ključa. Iz broja realizacija interfejsa Map trebalo bi da zaključite
koliko je ova alatka važna.
Da biste mogli bolje da shvatite M ape, pogledaćem 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 pronađen
}
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 " , "smeđa");
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 : smeđa
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 lakšeg prikazivanja m e-


toda toS trin g() redefinisana tako da ispisuje parove ključ-vrednost. Da bism o pokazali
da to funkcioniše, m a in () učitava 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 pronađe, a ona
vadi i kao rezultat vraća njem u p rid ru žen u vrednost ili n ull ako ga ne pronađe. M etoda
g e t ( ) upotrebljava verovatno najm anje efikasan način lociranja vrednosti koji se može
zamisliti: krene od vrha niza i m etodom e q u a ls() poredi sve ključeve redom . Ali poenta
je jednostavnost, a ne efikasnost.
Dakle, gornja verzija je poučna, ali ne i veom a efikasna, a im a i neprom enljivu ve-
ličinu, što nije prilagodljivo. Srećom, M ape u paketu ja v a.u til nem aju te problem e, a
m ožete ih zam eniti u gornjem prim eru.
Vežba 12: (1) Zam enite po jedan kontejner tipa HashMap, TreeMap o d nosno Linked-
HashMap u metodi m a in () program a AsocijativniNiz.java.
Vežba 13: (4) U potrebite AsocijativniNiz.java za prebrojavanje reči, pri čem u se String
m apira (preslikava) u Integer. Uslužnom m eto dom net.m indview.util.TextFile (iz ove
knjige) otvorite tekstualnu datoteku i podelite je na reči, p ri čem u su graničnici razm ak i
znakovi interpunkcije, i prebrojte reči u toj datoteci.

Performanse
Perform anse su temeljni problem pri radu s m apam a, jer je linearno pretraživanje me-
todom g e t( ) veoma spor način traženja ključa. Ovde pom aže brži kontejner H ashM ap.
Umesto sporog traženja ključa, on upotrebljava posebnu vrednost nazvanu k lju č za heši-
ranjc (engl. hash codc). Ključ za heširanje predstavlja način da se određena 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 heširanje. Kontejner H ashM ap uzim a h a s h C o d e () objekta i njom e brzo traga za
ključem. Tim e se dobija ogrom no poboljšanje perform ansi.6
Navešćemo osnovne realizacije interfejsa Map. Zvezdica kod H ashM ape pokazuje da
bi je trebalo podrazum evano izabrati (ako nem a drugih ograničenja), zato što je optim i-
zovana po brzini. Ostale realizacije im aju naglašenija druga obeležja, te su sporije od
H ashM ape.
Transform acija ključeva je najčešći način skladištenja 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 ište n ja n a d is k u i o n i će
v a m u š te d e ti n ajveći 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 eće.
66 4 Misliti na Javi

HashMap* Realizacija zasnovana na tabeli transformisanih ključeva. (Upotrebite je


umesto Hashtable.) Umetanje i pronalaženje parova obavlja u konstant-
nom vremenu. Performanse se mogu podesiti preko konstruktora koji
omogućuju zadavanje kapaciteta i faktora opterećenja tabele transformisa-
nih ključeva.
LinkedHashMap Poput HashMape, ali kada kroz nju iterirate, parove dobijate u redosledu
umetanja ili u redosledu najdavnijeg korišćenja [engl. Ieast-recently-used,
LRUJ. Samo malo sporija od HashMape, sem za iteriranje, kada je brža
zbog ulančane liste koja održava interni poredak.
TreeMap Realizacija napravljena na osnovu crveno-crnog stabla. Kada pogledate
ključeve ili parove, oni će biti u uređenom poretku (koji određuje Compa-
rable ili Comparator). Suština primene kontejnera TreeMap jeste da re-
zultate dobijate u uređenom poretku. TreeMap jejedina Mapa koja ima
metodu subM ap(), dakle jedina koja može da vrati deo stabla.
WeakHashMap Mapa slabih ključeva koji omogućuju da objekti na koje mapa upućuje
budu oslobodeni (sakupljeni u smeće); koristi se za rešavanje određenih
vrsta problema. Ako izvan mape ne postoje reference na određeni ključ,
on može biti pokupljen kao smeće.
ConcurrentHashMap Mapa koja bezbedno radi u višenitnom radu, a da nema zakljućavanje
zbog sinhronizacije. Ovo je objašnjeno u poglavlju Paralelno izvršavanje.
ldentityHashMap Mapa transformisanih ključeva koja za poređenje ključeva upotrebljava ==
umesto metode equals( J. Samo za rešavanje posebnih vrsta problema;
mje za opštu upotrebu.

Za ključeve 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 ključeva, 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 pokazaćem 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 može 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

/ / Ponašanje 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) ključeva iz Mape. Rezultate m etode v a lu es() lako ćete
štam pati zbog poboljšane podrške za štam panje u Javi SE5; ta m etoda pravi kolekciju od
svih vrednosti u M api. (Vodite računa o tom e da ključne reči m oraju biti jedinstvene, dok
vrednosti m ogu im ati duplikate.) Pošto M apa održava te kolekcije, svaka izmena u kolek-
ciji odražava se u njoj pridruženoj Mapi.
O statak program a pruža jednostavne prim ere svih operacija s M apam a i testira sve
osnovne vrste M apa.
Vežba 14: (3) Pokažite da java.util.Properties radi u gornjem program u.

SortedMap
Ako im ate neku realizađju interfejsa SortedMap (od njih je dostupna sam o TreeMap),
ldjučevi su zajam čeno u uređenom poretku, zbog čega sledeće m etode interfejsa Sor-
tedMap m ogu da pruže 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 najveći Jdjuč.
SortedMap subMap(odKljuč, doKljuč): Proizvodi prikaz dela m ape s ključevima od
ključa odKljuč, ukJjučivo, do ključa doKljuč, isključivo.
SortedMap headMap(toKey): Proizvodi prikaz dela m ape s ključevima m anjim od
ključa doKIjuč.
SortedMap tailMap(fromKey): Proizvodi prikaz dela m ape s ključevima jednakim ili
većim od Jđjuča odKljuč.
Ovaj prim er liči na PrimerZaSortedSet.java i pokazuje to dodatno ponašanje
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 može 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 uskladišteni po redosledu ključeva. Pošto je m apa TreeMap uređena,


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 iše sve ključeve radi brzine, ali tokom prolaska parove
daje u redosledu um etanja (System .out.println() iterira kroz m apu, pa rezultate prolas-
ka možete videti). Sem toga, realizacija LinkedHashMape može se u konstruktoru kon-
figurisati tako da upotrebljava algoritam najdavnijeg korišćenja (engl. least-recendy-used,
LRU) odnosno pristupanja, pa su na početku liste elementi kojima nije pristupano (te su
stoga kandiđati za uklanjanje). Tim e je olakšano pravljenje program a koji periodično či-
ste smeće cia bi se uštedeo 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 može 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 korišćenja:
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 eđutim , nakon (samo) prvih šest pristupanja u LRU verziji, poslednje tri
stavke prelaze na početak liste. Zatim, kada se ponovo pristupi stavci 0, ona prelazi na
začelje liste.

Transformisanje ključeva i ključevi za heširanje


U prim erim a iz poglavlja Č uvanje objekato, kao H ashM ap ključevi 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 ponašaju u ulozi ključeva.
U običajena greška je pravljenje sopstvenih klasa za ključeve u H ashM api, ali bez po-
trebnog koda. Na prim er, zamislite sistem za prognoziranje vrem ena koji objekte tipa
M edved pridružuje objektim a tipa Prognoza. To izgleda prilično 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 pronađen 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 pronađen k l j u č : Medved #3
* ///:-
670 Misliti na Javi

Svaki Medved dobija identifikacioni broj da biste u H ashM api mogli da pronadete
određeni objekat tipa Prognoza tako što ćete kazati: „Daj m i objekat tipa Prognozapri-
družen objektu #3 tipa Medved“. Klasa Prognoza sadrži objekat tipa boolean inicijalizo-
van m etodom java.util.random () i m etodu to S trin g() koja tum ači rezultate. M etoda
otkrivanjeProIeca() napravljena je procesom refleksije da generiše p rim erak klase Med-
ved ili njene potklase i da ga upotrebljava. To će nam dobro doći kasnije, kada za rešavanje
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 pronalaženje prognoze za objekat #3 tipa Medved (za koji m ožete vi-
deti da m ora biti u Mapi).
Sve izgleda veom a jednostavno, aii ne radi - ne m ože da se pronađe ključ za #3. Pro-
blem je to što se Medved autom atski nasleđuje od zajedničke korenske klase Object, pa se
za generisanje ključa za heširanje za svaki objekat upotrebljava m etoda h ash C o d e() klase
Object. O na za heširanje podrazum evano upotrebljava sam o adresu svog objekta. Zato
prvi prim erak Medved(3) ne proizvodi ključ za heširanje jednak ključu za heširanje dru-
gog prim erka Medved(3), koji sm o pokušali da upotrebim o za pretraživanje.
M ožda mislite da je dovoljno napisati odgovarajuću redefiniciju m etode h ash C od e().
Ali ni to neće funkcionisati sve dok ne uradite još nešto: redefinišete m etodu eq u a ls()
koja je takođe deo klase Object. HashMapa upotrebljava eq u a ls() prilikom utvrđivanja
da li je ključ jednak nekom ključu u tabeli.
Prava m etoda eq u als() m ora zadovoljiti sledećih pet uslova:
1. Refleksivna: Za svaki x, x.equals(x) treba da vraća true.
2. Simetrična: Za svaki x i y, x.equals(y) treba da vraća true ako i sam o ako
y.equals(x) vraća true.
3. Tranzitivna: Za svaki x, y i z, ako x.equals(y) vraća true i y.equals(z) vraća true,
onda x.equals(z) treba da vraća true.
4. Konsistentna: Za svaki x i y, svaki poziv x.equals(y) treba dosledno da vraća true ili
dosledno da vraća false, ukoliko se inform acije upotrebljene pri utvrđivanju jedna-
kosti objekata ne prom ene.
5. Za svaki x različit od null, x.equals(null) treba da vraća 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 ključeve, m orate redefinisati i
hashC od e() i eq u a ls(), kao što je urađeno u sledećem rešenju 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 funkcioniše.

public class DetektorProleca2 {


public static void main(String[] args) throws Exception {
DetektorProleca.otkrivanjeProleca(Medved2.class);
}
} /* Ispis:
mapa = {Medved #2=Rano proleče!, Medved #4=Još šest sedmica zime!,
Medved #9=Još šest sedmica zime!, Medved #8=Još šest sedmica zime!,
Medved #6=Rano proleče!, Medved #l=Još šest sedmica zime!, Medved
#3=Rano proleče!, Medved #7=Rano proleče!, Medved #5=Rano proleče!,
Medved #0=Još šest sedmica zime!}
Traženje prognoze za Medved #3
Rano proleče!
* ///:-

M edved2.hashCode() vraća broj medveda kao vrednost transform isanog ključa. 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 vraća 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 neće funk-
cionisati.
Iako izgleda kao da m etoda eq u a ls() proverava samo da li je njen argum ent instanca
od Medved2 (pom oću rezervisane reči instanceof, objašnjene u poglavlju Podaci o tipu),
instanceof zapravo potiho proverava i da li je objekat jednak null, pošto instanceof daje
rezultat false ako je njen levi argum ent jednak null. Ako je tip odgovarajući i nije null,
porede se vrednosti broj u oba objekta. Iz ispisa rezultata vidite da je ponašanje sada
ispravno.
Kada pravite klase za upotrebu u kontejneru tipa HashSet, m orate o bratiti pažnju na
iste stvari kao kada ih upotrebljavate kao ključeve u kontejneru tipa HashMap.

Način rada metode hashCodef )


Prethodni prim er je tek prvi korak u ispravnom rešavanju problem a. Iz njega vidite sle-
deće: ukoliko ne redefinišete 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 neće ispravno raditi s tim ključem. Da biste pronašli dobro rešenje problem a,
treba da razum ete šta se dešava u n u ta r strukture transform isanih podataka.
672 Misliti na Javi

Prvo, prisetite se zašto transform išem o ključeve: hteli sm o m ogućnost da jedan obje-
kat pronadem o pom oću drugog. To se m ože postići i pom oću kontejnera TreeMap ili čak
i realizacijom sopstvene Mape. N asuprot realizaciji s transform isanjem ključeva, naredni
prim er realizuje Mapu p om oću 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 objašnjava 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 ešta ključeve i vrednosti u odgovarajuće ArrayListe.
U skladu sa interfejsom Map, m ora da vrati stari ključ ili null ako stari ključ ne pronađe.
Takođe u skladu sa specifikacijama za interfejs Map, m etoda g e t ( ) daje n ull ako se
traženi ključ ne pronađe u m api SporaMapa. Ukoliko traženi ključ postoji, upotrebljava
se za pronalaženje num eričkog indeksa koji pokazuje njegovo m esto u Listi kljucevi i taj
broj se koristi kao indeks kojim se proizvodi pridružena vrednost iz Liste vrednosti.
O bratite pažnju na to da je kljuc u m etodi g e t ( ) tipa Object, a ne param etrizovanog tipa
K kao što biste mogli očekivati (i koji je naravno bio upotrebljen u p rim eru Asocijativ-
niNiz.java). Ovo je posledica tako kasnog ubacivanja generičkih tipova u J a v u - da su ge-
nerički 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. Međutim,
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 definišete 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 vraća ključeve i vrednosti. To je u
m etodi entrySet() iskorišćeno za pravljenje skupa parova ključ - vrednost. O bratite
pažnju 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 rešenje veom a jednostav-
no i naizgled funkcioniše 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 pruži u v id u Mapu, a ne da pravi njenu kopiju, i taj uvid
će om ogućiti modifikaciju prvobitne m ape (što kopija ne om ogućuje). Priliku da rešite
ovaj problem naći ćete u vežbi 16.
Vodite računa o tom e da m etoda e q u a ls () klase StavkaM ape m ora da proverava i
ključeve i vrednosti. Značenje m etode h a s h C o d e () biće opisano uskoro.
Predstavu sadržaja 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 () učitava se S poraM apa i zatim se prikazuje njen sadržaj.
Poziv m etode g e t( ) pokazuje da to funkcioniše.
Vežba 15: (1) Ponovite vežbu 13 uz upotrebu m ape SporaM apa.
Vežba 16: (7) Prim enite testove iz program a M aps.java na klasu S poraM apa da biste pro-
veriii kako funkcioniše. Popravite sve što u klasi SporaM apa ne funkcioniše korektno.
Vežba 17: (2) Realizujte ostatak interfejsa M ap za klasu SporaM apa.
Vežba 18: (3) Napravite SporSkup po uzoru na m apu SporaM apa.java.

Transformisanje ključeva zbog brzine


S poraM apa.java pokazuje da nije teško napraviti nov tip Mape. Ali kao što joj ime nago
veštava, SporaM apa nije baš brza, pa je verovatno ne bi koristio niko ko im a neku drugu
m ogućnost. Problem je pronalaženje ključa; ključevi se ne drže u određenom poretku, pa
se za njim a traga jednostavnom linearnom pretragom . Linearno pretraživanje je najspo-
riji način pronalaženja.
Razlog za transform isanje ključeva je brzina: zbog toga je pretraživanje brzo. Pošto je
brzina pronalaženja ključeva usko grlo, jedno od rešenja problem a jeste da se ključevi
drže u uređenom poretku i da se zatim pretraživanje obavlja m etodom
C ollectio n s.b in ary S earch () (u jednoj vežbi proći ć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 ože brzo pronaći. Najbrža struk tura za skladištenje grupe elemenata je niz, pa će
on biti upotrebljen za predstavljanje inform acija o ključu (rekao sam „inform acija o
ključu“, a ne sam og ključa). Budući da je veličina niza neprom enljiva, im am o problem :
želimo da sm estim o neodređen broj vrednosti u M apu, ali ako je broj ključeva određen
veličinom niza, kako ćem o to postići?
O dgovor je da taj niz neće sadržati ključeve. Iz objekta ključa biće izveden broj - indeks
u tom nizu. Taj broj je k lju č za heširanje (engl. hash code) koji daje m etoda h a s h C o d e () (u
rečniku računarske nauke nju nazivam o heš fu n k c ija ilifu n k c ija za transform isanje ključe-
va), defm isana u klasi O bject i verovatno redefinisana u vašoj klasi.
Da bi se rešio problem niza neprom enljive veličine, više ključeva m ože dati isti indeks.
Dakle, m ogu nastati sudari (engl. collisions). Stoga nije važno koliki je niz; u njem u će biti
mesta za ključeve za heširanje svih objekata ključeva.
Zato postupak pronalaženja vrednosti počinje izračunavanjem ključa za heširanje koji
se upotrebljava kao indeks niza. Kada biste m ogli jem čiti da neće biti sudaranja (što je m o-
guće ako im ate neprom enljiv broj vrednosti), onda biste imali savršenu fu n k c iju za trans-
form isanje ključeva, ali to je poseban slučaj.7 U svim drugim slučajevima, 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
način. Naravno, taj deo pretraživanja je m nogo sporiji, ali ako je funkcija za transfor-
misanje ključa dobra, u svakom odeljku liste biće tek nekoliko vrednosti. Stoga se umesto
pretraživanja cele liste, brzo skače na odeljak u kojem za pronalaženje vrednosti treba upo-
rediti tek nekoliko stavki. To je m nogo brže i zato je kontejner H ashM ap tako brz.
Pošto sada znate osnove transform isanja ključeva, m ožem o da realizujemo jednostav-
nu M apu s transform isanim ključevima:

/ / : 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 možete 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 možete 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, savršena funkcija za tran sfo rm isan je ključeva (heš funkcija) realizovana je u kontej-
n erim a E num M ap i EnumSet, zato što enum definiše 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]
* ///:-
Pošto ,,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 obično p rim broj.8
O bratite pažnju na to da se radi o ulančanoj listi L inkedList koja autom atski razešava su-
dare: svaka nova stavka se jednostavno dodaje na kraj liste u određenu kofu. Iako Java ne
dozvoljava pravljenje niza generičkih objekata, m oguće je napraviti refereticu na takav niz.
Ovde je podesno svesti naviše na takav niz, da bi se sprečilo d o d atn o svođenje 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 rezultujućeg broja u niz kofe
koristi se operator m odulo (%) i veličina niza. Ako se za m esto na koje rezultat treba uba-
citi dobije null, to znači da nem a elem enata koji su transform isanjem dospeli na to mesto,
pa se pravi nova ulančana lista (LinkedList) za čuvanje objekta koji je transform isanjem
upravo dospeo na njega. M eđutim , norm alan postupak se sastoji od toga da se u listi po-
traže duplikati, pa ako postoje, stara vrednost se smešta 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 oću ključa, izračunava indeks u nizu kofe na
isti način kao put( ) (tim e se jem či da ćete dospeti u isti odeljak liste). Ukoliko postoji
neka ulančana lista, u njoj se traži vrednost koja odgovara datom ključu.
Imajte u vidu podatak da ova realizacija nije optim izovana za perform anse, nego sam o
pokazuje operacije koje obavlja m apa transform isanih ključeva. Pogledajte izvorni kod za
java.util.HashMap ako vas zanim a optim izovana realizacija. Takođe, jednostavnosti radi,
JednostavnaHashMapa metodi en tryS et() pristupa jednako kao SporaMapa, što je pre-
više pojednostavljeno i ne bi funkcionisalo u Mapi opšte nam ene.
Vežba 19: ( 1) Ponovite vežbu 13 uz upotrebu m ape JednostavnaHashMapa.
Vežba 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 veličina kofa za heširanje, i novije realizacije heši-
ranja u Javi upotrebljavaju veličinu jednaku nekom step en u b roja dva (a to je rezultat opsežnog 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 ajćešća 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

Vežba 21: (2) Prepravite program JednostavnaHashMapa tako da prijavljuje broj ,,pro-
ba“ p otrebnih kada dođe do sudara. D rugim rečima, koliko p u ta se m o ra pozvati n e x t()
za Iteratore koji prolaze ulančanim listam a u potrazi za p o d u d arn im elementima?
Vežba 22: (4) Realizujte m etode clea r() i rem ove() za kontejner JednostavnaHashMapa.
Vežba 23: (3) Realizujte ostatak interfejsa Map za kontejner JednostavnaHashMapa.
Vežba 24: (5) Po uzoru na prim er u program u JednostavnaHashMapa.java, napravite i
testirajte JednostavanHashSkup.
Vežba 25: (6) U m esto da za svaku kofu upotrebljavate po jedan Listlterator, prepravite
klasu StavkaMape tako da bude sam ostalna jednostruko ulančana lista (svaka Stavka-
Mape treba da im a vezu unapred ka sledećoj klasi StavkaMape). Prepravite ostatak koda
u program u JednostavnaHashMapa.java tako da novi pristup ispravno funkcioniše.

Redefinisanje metode hashCodef)


Pošto sada razum ete kako radi transform isanje ključeva, im a smisla da i sami napišete
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 određenog H ashM ap objekta, a taj kapacitet se
m enja u zavisnosti od napunjenosti kontejnera i fakto ra opterećenja (objasnićem o taj ter-
m in kasnije). Stoga će vrednost koju proizvede vaša 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, proračun se svo-
di na operaciju m odulo s veličinom niza kofa).
Pri pravljenju m etode h a s h C o d e () najvažnije je da ona daje istu vrednost za određeni
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 ( ) umeće u
H ashM apu, a drugu kada se m etodom g e t( ) vadi iz nje, nećete moći da pronalazite i va-
dite objekte iz m ape. Stoga ako se rezultat vaše 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 različitogključa, jer se generiše drugačiji h a s h C o d e ().
Sem toga, hashC ode( ) verovatno nećefegenerisati 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 slučaju ne m ožete generisati ključ identičan 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-
ležje: ukoliko program im a više S trin g objekata koji sadrže identične 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 identične. 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
* ///:-
Očigleđno je da se hashC od e() za tip String izračunava na osnovu sadržaja 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 sadržaja objekta. Ne zaboravite da ta vrednost ne m ora biti
jedinstvena — bavite se brzinom , a ne jedinstvenošću - ali kom binacija m etoda
h ash C od e() i eq u a ls() m ora u potpunosti odrediti identitet objekta.
Pošto se hashC od e() dodatno obraduje pre pretvaranja u indeks kofa, opseg vredno-
sti rezultata te m etode nije važan; dovoljno je da ona generiše ceo broj (int).
Ima tu još nešto: dobra m etoda hashC ođ e() treba da daje ravnom erno raspodeljene
vređnosti. Ako se te vrednosti negde gomilaju, onda će kontejner HashMap ili HashSet
na nekim mestim a biti gušće popunjen i neće biti onoliko brz kao što m ože biti uz ravno-
m erno raspodeljene rezultate funkcije za heširanje.
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 različit od
nule, recimo 17.
2. Za svako značajno polje f u objektu (tj. svako polje koje m etoda eq u a ls() uzim a u
obzir) izračunajte celobrojni (int) hashC od e() c:

Tip polja Algoritam za izračunavanje


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 goreizračunate ključeve 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 ključeve 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 ;
/ / Upotrebićemo 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
Tražim 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 sadrži jedan znakovni niz (String) i jedan id koji predstavlja


broj objekata tipa PrebrojanZnakovniNiz koji sadrže identičan znakovni niz. Prebroja-
vanje se obavlja u konstruktoru, iteriranjem kroz statičnu listu ArrayList u kojoj su uskla-
dišteni 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, dešavalo bi se da različite vrednosti
dobiju duplikate istog ključa.
U m etodi m a in (), pom oču istog znakovnog niza napravljeno je više 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 unutrašnji poredak (ne može se uočiti nikakva pravilnost), a zatim se svaki
ključ traži pojedinačno, kako bi se pokazalo da m ehanizam pronalaženja radi kako treba.
Kao drugi prim er, razm otričem 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 odložena 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 poređenja, tako da proizvodi sekvencu uređenu
prvo po stvarnom tipu, zatim po im enu ako ono postoji, i najzad po redosledu pravljenja.
U ovom prim eru videćete 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
* ///:-

Pošto svi ovi ljubim ci im aju im ena, uređuje se prvo po tipu, zatim po im enu i najzad
po im enu u n u ta r njihovog tipa.
N apisati odgovarajuće 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 oći pronaći ć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).
Vežba 26: (2) U program PrebrojanZnakovniNiz dodajte char polje koje se takođe inici-
jalizuje u konstruktoru i prepravite m etode h ash C od e() i eq u a ls() tako da obuhvate
vrednost ovog polja.
Vežba 27: (3) Prepravite m etodu h ash C od e() u program u PrebrojanZnakovniNiz.java
tako što ćete ukloniti kom binaciju sa id, i pokažite da PrebrojanZnakovniNiz i dalje radi
kao ključ. Zašto ovaj pristup ne valja?
Vežba 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 opšte nam ene.

Izbor realizacije
Dosad je trebalo da shvatite sledeće: iako postoje sam o ćetiri osnovna tipa kontejnera
- M ap, List, Set i Q ueue - svaki od tih interfejsa im a više realizacija. Ako vam treba funk-
cionalnost određenog interfejsa, kako da izaberete njegovu realizaciju?
Svaka realizacija ima svoja obeležja, prednosti i nedostatke. Na prim er, na slici s poćet-
ka ovog poglavlja vidite da je obeležje klasa Hashtable, Vector i Stack to što su nasleđene
iz prethodnih verzija Jave (engl. legacy), kako se stari kod ne bi rušio zbog prevođenja 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
načinu prim anja i vraćanja vrednosti (važnost toga objasnićem o u poglavlju Paralclno
izvršavanje).
Razlika između kontejnera često se svodi na to kakvi su ,,u pozadini“- tj. koje strukture
podataka fizički realizuju željeni interfejs. Na prim er, pošto ArrayList i LinkedList reali-
zuju interfejs List, elem entarne List operacije su iste bez obzira na to koju od njih upo-
trebite. M eđutim , u pozadini A rrayListe je niz, dok je LinkedList realizovana na način
uobičajen za dvostruko ulančanu listu, kao grupa pojedinačnih objekata koji sadrže
684 Misliti na Javi

podatke i reference na prethodni i sledeći elem ent u listi. Upravo zato, ako nam eravate da
m nogo um ećete i uklanjate elem ente usred liste, koristite LinkedList. (LinkedList im a i
dodatn u funkcionalnost, utvrđenu u listi AbstractSequentialList.) Ali ako vam to nije
potrebno, uzm ite listu ArrayList koja je obično brža.
Kao drugi prim er, Set se može realizovati kao TreeSet, HashSet ili LinkedHashSet.9
Ponašanje svakog od tih skupova je drugačije: HashSet je podesan za tipičnu u p o treb u i
najbrže obavlja pretraživanje, LinkedHashSet čuva parove u poretku um etanja, a u po-
zadini skupa TreeSet nalazi se m apa TreeMap koja uvek daje uređen skup. Realizaciju bi-
rate u skladu s ponašanjem koje vam je potrebno.
Ponekad različite realizacije odredenogkontejnera im aju neke zajedničke operacije, ali
su perform anse tih operacija različite. U tom slučaju, realizaciju birate na osnovu toga
koliko često koristite određenu operaciju i koliko brza ona m ora da bude. U takvim sluča-
jevim a se o razlikama izm eđu realizacija kontejnera m ože zaključivati 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 ujednačenost
ispitivanja. Sledeći program pravi osnovnu klasu od koje m ožete napraviti listu anonim -
nih unutrašnjih klasa, po jednu za svako ispitivanje. Postupak ispitivanja se sastoji od po-
zivanja svake od tih unutrašnjih klasa. Tim e je olakšano 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 različito ispitivanje sledim o tipićan pri-
stup Tem plate M ethod, u ovom slučaju je osnovni kod (onaj koji se ne m enja) u zasebnoj
klasi Tester.10 Tip kontejnera koji se ispituje opisan je generičkim 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 .
/ / Vraća 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 skladišti 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 sadrži 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 određuje 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 različitim
param etrim a ispitivanja (objektom tipa Paramlspita), pa Paramlspita sadrži i dve sta-
tične 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 enično sadrži vrednosti velicina i
petlji, a druga verzija prim a listu iste vrste, sem što su vrednosti sm eštene u znakovne ni-
zove (objekte tipa String) - pa se može upotrebljavati za raščlanjivanje 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 određenoj m etodi Tester.pokreni() (to su
prigodne preklopljene generičke m etode zahvaljujući kojima ne m orate toliko da kucate)
prosledite kontejner koji treba ispitati i Listu Test objekata. Tester.pokreni() poziva od-
govarajući preklopljeni konstruktor, a zatim poziva m etodu vrem enskoIspit() koja
izvršava svako ispitivanje navedeno u listi za taj kontejner. vrem enskoIspit() ponavlja
svako ispitivanje za svaki od Paramlspita objekata u listi listaParam. Pošto se listaParam
inicijalizuje pom oću statičnog niza podrazumevaniParam, zadavanjem novih vrednosti
niza podrazumevaniParam možete da prom enite param etre listaParam za sva ispiti-
686 Misliti na Javi

vanja, a za jedno ispitivanje param etre listaP aram m ožete da prom enite prosleđivanjem
liste listaP aram prilagođene 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;
}
/ / Pomoćne 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 statične
vrednosti sirinaPolja. Metoda prikaziZaglavlje() form atira i ispisuje zaglavlje s podaci-
ma iz svakog ispitivanja.
Ako vam je potrebna specijalna inicijalizacija, redefinišite m etodu in icijalizu j(). O na
proizvodi inicijalizovan objekat kontejner odgovarajuče veličine - možete modifikovati
postojeći objekat kontejner ili napraviti novi. U m etodi isp it() vidite da se rezultat hvata
u lokalnoj referenci nazvanoj kontejner, što om ogućuje da uskladišteni član kontejner
zam enite p otp u n o drugačije 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 izračunavanje broja nanosekundi potrebnih za svaku
operaciju. Imajte u vidu da m etoda S ystem .nanoT im e() obično daje vrednosti čija gra-
nularnost prem ašuje jedan (i m enja se u zavisnosti od računara i operativnog sistema), a
to će prouzrokovati određenu količinu šum a u rezultatim a.
Rezultati se m ogu m enjati u zavisnosti od računara na kojem se ispitivanja obavljaju;
ova ispitivanja su nam enjena sam o za relativno poređenje perform ansi različitih kontej-
nera, a ne za apsolutno m erenje tih perform ansi.

Performanse različitih Lista


Evo ispitivanja perform ansi osnovnih operacija u listam a. Poređenja radi, prikazane su i
najvažnije operacije u redovim a za čekanje (Q ueue). Za ispitivanje svake klase kontejnera
napravljene su dve zasebne Iiste testova. U ovom slučaju, operacije u redovim a za čekanje
odnose se sam o na ulančane 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
/ / nasumičnog 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
/ / nasumičnog 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 ponašanja 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;
}
/ / Pomočna 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 deća 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 briše Listu i zatim je popunjava do zadate veličine li-
ste. Zato je poziv m e to d e c le a r() deo ispitivanja i može uticati na izm ereno vreme, naroči
to u malim ispitivanjima. Iako prethodni rezultati izgledaju prilično razum no, struktura
za ispitivanje bi se mogla preraditi tako da pozivpriprem ne m e to d e - što bi, u ovom sluča-
ju, obuhvatilo i poziv m etode c le a r() - bude izvan petlje u kojoj se meri vreme.
Vodite računa o tom e da svako ispitivanje m ora tačno izračunati broj obavljenih ope-
racija i vratiti tu vrednost iz m etode is p i t( ), da bi m erenje vrem ena bilo tačno.
Poglavlje 17: Detaljno razmatranje kontejnera 693

U ispitivanjima operacija get i set upotrebljava se generator slučajnih brojeva za nasu-


m ično pristupanje Listi. Iz ispisa vidite da su za Listu napravljenu na osnovu niza i za
ArrayList ta pristupanja brza i ujednačenog trajanja bez obzita na veličinu liste, d o k za
ulančanu listu (LinkedList) trajanje pristupanja znatno rastc z a veče liste. O čigledno je
da ulančane liste nisu dobro rešenje ukoliko nam eravate da obavljate m nogo nasum ičnih
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 veličiru. 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 poveže nov elem ent i ne m ora da m enja ost?i -■■h liste, p a bi režijski tro-
škovi te operacije trebalo da b u d u približno jednaki, bez obziia a a veličinu 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 lančana lista (LinkedL isr; im a poseban algoritam
za krajnje lokacije Liste - tim e se povećava brzina kada se ulančana lista upotrebljava kao
red za čekanje (Queue). M edutim , ukoliko elem ente dodajete t : nklanjate u sredini liste,
prouzrokujete troškove nasum ičnog pristupanja koji se razlikuju za razne realizacije Lista
(kao što ste videli). Pošto su um etanja i uklanjanja na istoj loka 'i broj 5, trebalo bi da su
troškovi nasum ičnog pristupanja zaneinarivi i da vidim o sa o troškove um etanja i
uklanjanja, ali i da ne vidim o bilo kakvu posebnu optimizaciju za krajeve ulančane liste
(LinkedList). Iz rezultata vidite da su troškovi um etanja i uklanja:ija iz ulančane liste veo-
ma mali i da se ne menjaju u zavisnosti od veličine liste, ali su tre ovi (naročito um etanja)
u objekat tipa A rrayList veom a veliki i povećavaju se s veličinom liste.
Iz ispitivanja redova za čekanje (objekata tipa Queue) viditc icoliko brzo ulančana lista
umeće i uklanja elemente s krajnjih tačaka liste, što je optim abio za ponašanje redova za
čekanje.
O bično je dovoljno samo pozvati m etodu T ester.p o k ren i() i proslediti joj kontejner i
spisak ispitivanja. M eđutim , ovde m oram o redefinisati m etodu sn ic ija liz u j() da bi Lista
bila obrisana i ponovo popunjena pre svakog ispitivanja - inače bi Lista tokom raznih
ispitivanja izgubila kontrolu nad svojom veličinom. IspitivacLisfa nasleduje klasu Tester
i obavlja inicijalizaciju uz pom oć objekta tipa B rojackaL istalntegera. Redefinisana je i
pom oćna 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 oću anonim ne unutrašnje 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 uskladišteni 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 slučaju rnogu se obaviti sam o dva
ispitivanja, zato što elem ente ne možete 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 ičnog (nesekvencijalnog) pristupanja g e t ( ) i s e t ( ), Lista napra-


vljena na osnovu niza neznatno je brža od kontejnera tipa ArrayList, ali su iste te opera-
cije m nogo skuplje kada se koriste ulančane liste, pošto klasa LinkedList nije nam enjena
za operacije nasum ičnog (nesekvencijalnog) pristupanja.
Klasu Vector treba izbegavati; ona je u biblioteci sam o da bi se obezbedila podrška za
stari kod (u ovom program u deluje sam o zato što je radi kom patibilnosti s novim kodom
prerađena tako da postane Lista).
CopyOnWriteArrayList je posebna realizacija klase List koja se upotrebljava u pro-
gram iranju za paralelno izvršavanje. N ju ćem o razm otriti u poglavlju Paralelno izvrša-
van je.V t rovatno je najbolja politika uzeti ArrayList za podrazum evani tip kontejnera i
preći 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 veličine, upotrebite Listu - m etodom Arrays.asList() - napra-
vljenu na osnovu niza ili pravi niz, ako je potrebno.
Vežba 29: (2) Izm enite program PerformanseLista.java tako da Liste um esto Integera
skladište objekte tipa String. Za pravljenje niza ispitnih vrednosti upotrebite Generator
iz poglavlja N izovi.
Vežba 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.
Vežba 31: (5) Napravite kontejner koji kapsulira niz objekata tipa S tring i dozvoljava
sam o dodavanje i vađenje znakovnih nizova, pa se tokom upotrebe ne postavlja pitanje
svođenja na druge tipove. Ako unutrašnji niz nije dovoljno velik za sledeće dodavanje,
kontejner treba da autom atski prilagodi njegovu veličinu. U m etodi m a in ( ) uporedite
perform anse tog kontejnera i objekta tipa A rrayL ist< String> .
Vežba 32: (2) Ponovite p reth od nu vežbu za kontejner celih brojeva (in t) i uporedite nje-
gove perform anse i perform anse objekta tipa A rrayL ist< Integer> . Neka poređenje per-
form ansi obuhvati i postupak povećavanja svakog objekta u kontejneru za 1.
Vežba 33: (5) Napravite objekat tipa FastTraversalLinkedList koji interno upotrebljava
ulančanu listu (objekat tipa LinkedList) za brza dodavanja i uklanjanja, a objekat tipa Ar-
rayList za brza prolaženja kroz kontejner i operacije g e t ( ). Ispitajte ga prepravljenim pro-
gram om Perform anseL ista.java.

Opasnosti od mikropoređenja performansi


Kada pišete program e za m ikropoređenjeperform ansi, čuvajte se da ne pretpostavljate pre-
više i toliko suzite testove da oni m ere vreme izvršavanja sam o onih operacija koje se ispi-
tuju. Takođe, m orate paziti na to da ispitivanje bude dovoljno dugačko da njegovi
rezultati bu du statistički pouzdani i uzm ite u obzir to da se neke Java HotSpot tehnologije
uključuju tek nakon što se program izvršava određeno vreme (to se m ora uzeti u obzir i
kad se prave kratki program i).
Rezultati će se razlikovati u zavisnosti od računara i JVM-a koje upotrebljavate, pa bi
trebalo da sam i obavite pretho dn a ispitivanja kako biste proverili da li su rezultati slični
Poglavlje 17: Detaljno razmatranje kontejnera 695

onim a navedenim u ovoj knjizi. Ne pokušavajte da izmerite apsolutna trajanja izvrša-


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 ožete. Java se isporučuje 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 proizvođača, 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 između
nule i jedinice, uključivo ili isključivo s brojem 1? M atem atički rečeno, jesu li njeni rezul-
tati u intervalu (0,1), ili [0,1], ili (0,1] ili [0,1)? (Uglasta zagrada znači ,,obuhvata“, a
okrugla ,,ne obuhvata") M ožda ć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 )
; / / Pokušavaj 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)
; / / Pokušavaj 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 pokrečete upisivanjem jedne od sledečih 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 slučaja m oraćete ručno da prekinete izvršavanje program a, pa izgleda kao da


M ath.random () nikada ne daje ni 0.0 niti 1.0. Ali tu vas ovakav eksperim ent može pre-
variti. Ako uzm ete u obzir da izm eđu 0 i 1 postoje oko 262 različita dvostruka razlomka,
verovatnoća da će se eksperim entalno dobiti bilo koji od tih pojedinačnih brojeva tako je
mala da prevazilazi životni vek računara, pa čak i eksperim entatora. Ispostavlja se da me-
toda M ath.random () m eđu svojim rezultatim a daje i 0.0. Ili, m atem atički rečeno, njeni
rezultati su u intervalu [0,1). Dakle, m orate pažljivo analizirati svoje eksperim ente i
shvatiti njihova ograničenja.

Izbor skupa (realizacije interfejsa Set)


U zavisnosti od traženog ponašanja, m ožete izabrati TreeSet, HashSet ili LinkedHash-
Set. N aredni program za ispitivanje pokazuje različite 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, naročito pri
dodavanju elem enata i traganju za njim a (m etodom co n ta in s()), što su dve najvažnije
operacije. TreeSet postoji zato što svoje elem ente održava u uređenom poretku, pa ga
upotrebljavam o sam o onda kada nam treba uređen Set. Zbog unutrašnje stru k tu re pot-
rebne za uređivanje i zato što se iteriranje češče izvodi, ono je obično brže u kontejneru
tipa TreeSet nego tipa HashSet.
O bratite pažniu na to da su um etanja u skupu LinkedHashSet dugotrajnija nego u
skupu HashSet; to važi zato što održavanje ulančane Iiste prouzrokuje dođatne troškove,
jer se dodaje onim a za kontejner s transform isanjem ključa.
Vežba 34: ( 1) Izm enite PerformanseSkupova.java tako da objekti tipa Set skladište 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 različite 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 eđutim , pretraživanje je po pravilu m nogo brže od
um etanja, što je dobro zato što se stavke obično m nogo češće traže nego što se umeću.
Performanse mape Hashtable približno su jednake onim a m ape HashMap. Pošto je
HashMap predviđena da zameni Hashtable, te zato u pozadini upotrebljava isti m ehani-
zam za skladištenje i pretraživanje (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 ogućuje pravljenje uređene liste. Stablo (engl. tree) je uvek uređeno, pa ek-
splicitno sortiranje nije neophodno. Kada pop un ite TreeMap, m ožete pozvati m etodu
k eyS et() koja će dati skup (Set) ključeva, a zatim m etodu toA rray() koja će dati njihov
niz. Zatim možete pozvati statičnu m etod u Arrays.binarySearch() koja brzo pronalazi
objekte u tom uređenom nizu. Naravno, to im a smisla sam o ako je ponašanje kontejnera
HashMap neprihvatljivo, pošto baš HashMap brzo pronalazi ključeve. 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 uređena mapa.
Za um etanja je LinkedHashMap sporiji od m ape HashMap, zato što pored strukture
podataka s transform isanim ključevima održava i ulančanu listu (da bi očuvao redosled
um etanja). Ali zbog te liste brža je iteracija.
Performanse kontejnera IdentityHashM ap razlikuju se zato što on za poređenja upot-
rebljava ==, a ne m etodu eq u a ls(). WeakHashMap je opisana u nastavku poglavlja.
Vežba 35: (1) Izmenite program PerformanseMapa.java tako da obuhvati ispitivanja
kontejnera SporaMapa.
Vežba 36: (5) Izmenite kontejner SporaMapa tako da um esto dve ArrayListe ima jedan
kontejner ArrayList ćiji su objekti tipa StavkaMape. Dokažite 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 unošenja svakog para poziva s o r t( ), a izm enite i me-
todu g e t ( ) tako da za pronalaženje ključa koristi ColIections.binarySearch( ). Uporedite
perform anse nove verzije i starih verzija.
Vrežba37: (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 može se ručno podesiti tako da ima bolje perform anse u kon-
kretnoj aplikaciji. M orate poznavati sledeću term inologiju da biste shvatili od ćega zavise
perform anse prilikom podešavanja kontejnera HashMap:
K a pa citet. Broj kofa u tabeli.
P očetni ka p a c ite t. Broj kofa nakon pravljenja tabele. HashMap i HashSet imaju kon
struktore koji oniogućuju zadavanje početnog kapaciteta.
V eličina: Broj stavki tren u tn o u tabeli.
Poglavlje 17: Detaljno razmatranje kontejnera 701

F a k to r opterećenja: veličina/kapacitet. Faktor opterećenja 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 pretraživanje (ali će usporiti postupak prolaska kroz kontejner
iteratorom ). HashMap i HashSet im aju konstruktore koji om ogućuju zadavanje fak-
tora opterećenja, pa kada se ovaj faktor opterećenja dostigne, kontejner će autom atski
približno dvaput povećati kapacitet (broj kofa) i preraspodeliće postojeće objekte u
novi skup kofa (to se naziva ponovno heširanje, tj. ponovno transform isanje ključeva).
Podrazum evani faktor opterećenja kontejnera HashMap iznosi 0,75 (ponovno trans-
fo rm isa n je ključeva kontejncra pokreće se tek kada se p opuni tri četvrtine tabele). Izgleda
da je to d o bar kom prom is izm eđu utrošenog vrem ena i prostora. Veći faktor opterećenja
sm anjuje prostor potreban za tabelu ali povećava troškove pretraživanja, što je važno
pošto je pretraživanje - zajedno s g e t ( ) i p u t ( ) - najčešća operacija.
Ukoliko znate da ćete u kontejneru tipa HashMap skladištiti m nogo stavki, napravite
ga s dovoljno velikim početnim kapacitetom da biste sprečili dodatne troškove zbog au-
tom atskog ponovnog transform isanja ključeva.11
Vežba 38: (3) Potražite klasu HashMap u IDK dokum entaciji. Napravite kontejner tipa
HashMap, popunite ga elem entim a i izračunajte faktor opterećenja. Ispitajte brzinu pre-
traživanja ove mape, zatim pokušajte da povećate brzinu tako što ćete napraviti nov kon-
tejner tipa HashMap većeg početnog kapaciteta i kopirati stare m ape u novu, a potom
ponovo pokrenite svoje ispitivanje brzine nove mape.
Vežba 39: (6) Klasi JednostavnaHashMapa dodajte privatnu m etodu rehash() koja se po-
ziva kada faktor opterećenja premaši 0,75. Tokom ponovnog transform isanja ključeva ud
vostručite broi kofa, a zatim pronadite prvi veći prim t>roj i neka to bude novi broj kofa.

Uslužne metode
Za kontcjnere postoji više sam ostalnih uslužnih m etoda, napisanih u obliku statičnih 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 ’ sledećoj tabeli navodim o ostale (sinhronizovane i
neprom enljive uslužne m etode biće opisane u narednim odeljcima). U tabeli su generički
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 vclićin 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 slućaj. 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 dinamički bezbednu (u pogledu tipova) realiza-


Kolekcija<T>, Class<T> tip) ciju Kolekcije ili nekog njenog konkretnog podtipa. Kori-
checkedList( stite ih kada nije moguće upotrebiti statički proverenu
List<T>, Class<T> tip) verziju.
checkedM ap(M ap<K,V>,
Class<K> keyType,
Class<V> valueType) U poglavlju Generički tipovi ove metode su bile navedene
checkedSet(Set<T>, pod naslovom „Dinamička 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 najveći ili najmanji element argumenta koristeći me-
m in(Kolekcija) todu prirodnog poredenja objekata u Kolekciji.
m ax(Kolekcija, Komparator) Daje najveći ili najmanji element Kolekcije koristeći Ko-
m in(Kolekcija, Komparator) mparator
indexOfSubList(List izvor. Proizvodi poćetni indeks prvog mesta na kojem se
List odredište) odredište pojavljuje unutar izvora ili -1 ako takvog mesta
nema.
lastlndexO fSubList(List izvor. Proizvodi početni indeks poslednjeg mesta na kojem se
List odredište) odredište 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) Obrće redosied svih eiemenata liste.
reverseOrder( ) Vraća Kom parator koji obrće prirodni poredak kolekcije
reverseOrder( objekata koja realizuje interfejs Comparable<T> Druga
Komparator<T>) verzija obrće redosled datog Kom paratora
rotate(List, int udaljenost) Premešta sve elemente unapred za udaljenost;
one s kraja vraća na početak.
shuffle(List) Nasumično permutuje datu listu. Prvi obiik ima sopstveni
shufflefList, Random) izvor siučajnih 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> odredište. Kopira elem ente iz izvora u odredište
List<? extends T> izvor)
swap(List, int i, int j) Zamenjuje elemente na mestima i i j u listi List Veiovatno
brža od one koju možete sami da napišete.
fill(List<? super T>, T x) Zamenjuje sve elemente liste objektom x
Poglavfje 17: Detal)no razmatranje kontejnera 703

nCopies{int n, T x) Vraća nepromenljivu listu List<T> veličine n čije sve refe-


rence upućuju na objekat x
disjoint(Kolekcija, Kolekcija) Vraća true ako date kolekcije nemaju zajedničkih
elemenata.
frequency(Kolekcija, Object x) Vraća broj elemenata Kolekcijejednakih objektu x.
emptyList( ) Vraća nepromenljivu praznu listu, mapu ili skup Oni su
emptyMap( ) generički, pa će rezultujuća Kolekcija biti
emptySet( ) parametrizovana u željeni tip.
singletonfT x) Vraća 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 sadrži elemente u poretku
kojim ih vraća (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 uređena 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 prikazaćemo osnove korišćenja uslužnih m etoda iz gornje tabele:

//: kontejneri/UsluzneMetode.java
// Jednostavni primeri usiužnih 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 pomoću 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]
* ///= -
Ponašanje svake uslužne m etode vidi se iz ispisa rezultata. O bratite pažnju na razliku
izm eđu rezultata m e to d a m in () im a x ( ) uzString.CASE_INSENSITIVE_ORDERCom-
parator; razlog je zanem arivanje razlike izm eđu velikih i malih slova.

Uređivanje i pretraživanje lista (realizacija interfejsa List)


Uslužne m etode za sortiranje i pretraživanje lista im aju ista im ena i potpise kao one za
uređivanje nizova objekata, ali to su statične 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 pomoću 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 kraćena: " + 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 ]
Skraćena: [ 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]
Uređena: [ 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
Uređena 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 pretraživanju i uređivanju nizova, i ovde važi sledeće: ukoliko listu uredite po-
m oću nekog kom paratora, pom oću istog kom paratora m orate obaviti i b inarno pretraži-
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 ično
m enja redosled elemenata liste. Jedan L istlterator je napravljen na određenom mestu ispre-
turane (engl. shuffleđ) liste i upotrebljen za uklanjanje elemenata od tog mesta do kraja liste.
Vežba 40: (5) Napravite klasu s dva objekta tipa S tring i realizujte u njoj interfejs Com -
p arab le tako da poređenje uzima u obzir sam o prvi objekat tipa String. Popunite jedan
niz i jedan objekat tipa A rrayList objektim a vaše klase, koristeći generator RandoinG e-
nerator. Pokažite 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 pokažite da je sortiranje opet ispravno.
Obavite i binarno pretraživanje pom oću svoje realizacije interfejsa C om parator.
V ežba41: (3) Izmenite klasu iz prethodne vežbe tako da radi s kontejnerim a tipa H ashSet
i kao ključ u kontejnerim a tipa H ashM ap.
V ežba42: (2) Izmenite vežbu 40 tako da lista bude sortirana abecedno.

Kako kolekciju ili mapu učiniti nepromenljivom


Često je pogodno imati verziju kolekcije ili m ape samo za čitanje. Klasa C ollections om o-
gućuje da to uradite prosledivanjem originalnog kontejnera m etodi koja vraća njegovu
verziju samo za čitanje. Ta m etoda ima više varijanata: za kolekcije (ako datu kolekciju ne
možete tretirati kao konkretniji tip), liste, skupove i mape. U sledećem prim eru prikazan
je pravilan način 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
/ / Pomoću 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 određeni tip ne prouzrokuje proveru u vreme pre-
vodenja, ali nakon što se ta transform acija obavi, poziv bilo koje m etode koja menja
sadržaj tog kontejnera prouzrokuje U n su pp orted O peratio n E x cep tio n .
708 Misliti na Javi

U svakom slučaju, 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 učitavanja, najbolje je zam eniti postojeću re-
ferencu kontejnera referencom koju proizvodi poziv ,,unm odifiable“ m etode. Time izbe-
gavate rizik od nehotičnog pokušaja m enjanja sadržaja nakon što ste ga učinili
neprom enljivim . S druge strane, ova alatka om ogućuje i da prom enljivi kontejner zadrži-
te u obliku privatnog člana određene klase i da iz poziva neke m etode vratite referencu tog
kontejnera sam o za čitanje. Dakle, kontejner m ožete izm eniti iznutra iz klase, a svi drugi
m ogu sam o da ga čitaju.

Sinhronizovanje kolekcije ili mape


Rezervisana reč synchronized čini važan deo višenitnog izvršavanja, ali je ona toliko kom -
plikovana da je nećem o načinjati do poglavlja Paralelno izvršavanje. Ovde navodimo
sam o to da klasa Collections m ože autom atski da sinhronizuje ceo kontejner. Sintaksa je
slična ,,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 odgovarajuće ,,synchronized“ me-


tode, kao u gornjem prim eru. Tako se ne m ože dogođiti da slučajno eksponirate ne-
sinhronizovanu verziju.

Brzo otkazivanje
Javini kontejneri imaju i m ehanizam koji sprečava da više procesa istovremeno menja
sadržaj kontejnera. Problem nastaje ako ste usred iteriranja kroz kontejner, a neki drugi
proces uskoči i um etne, ukloni ili izmeni neki objekat u tom kontejneru. Možda ste taj ele-
Poglavfje 17: Detaljno razmatranje kontejnera 709

m ent kontejnera već prošli, m ožda je on ispred vas, m ožda će se kontejner sm anjiti nakon
što pozovete m etodu s iz e () - scenarija za katastrofu im a koliko hoćete. Biblioteka Java
kontejnera upotrebljava m ehanizam brzog otkazivanja (engl. fail-fast); u kontejneru traži
izm ene koje nije prouzrokovao vaš proces lično. Ako otkrije da neko drugi m enja sadržaj
kontejnera, odm ah generiše izuzetak ConcurrentModificationException. Taj aspekat se
naziva „brzo otkazivanje“ - problem se ne traži naknadno, nekim složenijim algoritm om .
M ehanizam brzog otkazivanja je sasvim lako videti u praksi - dovoljno je napraviti ite-
rato r i zatim nešto 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 ponašanje 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 nešto sm ešteno u kontejner nakon pribavljanja iteratora
od tog kontejnera. M ogućnost da dva dela program a m enjaju isti kontejner proizvodi
neodređeno stanje, pa vas izuzetak obaveštava kako bi trebalo da izmenite kod —u ovom
slučaju, 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 sadrži skup klasa koje om ogućuju veću fleksibilnost u
sakupljanju smeća. Te klase su naročito 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 sakupljaču smeća pruža drugačiji
nivo indirekcije ukoliko je objekat o kojem se radi dostižan sam o preko jednog od tih Re-
ference objekata.
Ako je objekat d ostiža n , m ože se pronaći negde u program u. To m ože značiti da im ate
običnu referencu steka koja upućuje pravo na objekat, ali mogli biste im ati i referencu
objekta koji sadrži referencu objekta o kojem se radi; tih međuveza m ože biti mnogo.
710 Misliti na Javi

Ukoliko je objekat dostižan, sakupljač smeća ga ne m ože osloboditi jer ga program još
uvek koristi. Ako objekat nije dostižan, ne postoji način da ga program upotrebi, pa je
bezbedno sakupiti ga u smeće.
Objekte tipa Reference koristite kada i dalje hoćete da čuvate referencu određenog ob-
jekta - taj objekat hoćete da dosegnete - ali takođe hoćete da om ogućite sakupljaču smeća
da taj objekat oslobodi. Dakle, postoji način da objekat koristite, ali ako se ukaže mo-
gućnost da se m em orija iscrpe, dozvoljavate da taj objekat bude oslobođen.
To se postiže korišćenjem objekta tipa Reference kao posrednika (engl. p roxy ) između
vas i obične reference. Sem toga, taj objekat ne sme im ati obične reference (one koje nisu
om otane u Reference objekte). Ako sakupljač smeća otkrije da je određeni objekat do-
stižan preko obične reference, taj objekat neće biti oslobođen.
Reference tipa SoftReference, VVeakReference i PhantomReference sve su ,,slabije“ i
odgovaraju različitim nivoima dostižnosti. Meke reference (engl. soft references) služe za
realizovanje keša, tj. ostava koje čuvaju m em oriju. Slabe reference (engl. weak references)
služe za realizovanje „kanonizujućih m apiranja (preslikavanja)" - gde se instance objekata
m ogu istovrem eno upotrebljavati na više mesta u program u, da bi se uštedela m em orija -
što ne sprečava da njihovi ključevi (ili vrednosti) budu sakupljeni u smeće. Fantomske re-
ference (engl. p h a n to m references) služe za planiranje operacija čišćenja „pre (memorijske)
sm rti“ na fleksibilniji način nego što bi to bilo moguće s Javinim m ehanizm om finalizacije.
Reference tipa SoftReference i WeakReference m ožete ali i ne m orate smestiti u ,,red
referenci za čekanje“ (ReferenceQueue) - tako je nazvan uredaj za akcije čišćenja ,,pre
(m em orijske) sm rti“) - dok se PhantomReference m ože 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), videćete da objekti bivaju sakupljani u
smeće iako im i dalje m ožete pristupati preko objekta tipa Reference - referencu objekta
pribavljate m etodom g e t ( ). Videćete i to da ReferenceQueue uvek proizvodi null objekat
tipa Reference. Da bi taj objekat postao upotrebljiv, nasledite određenu klasu Reference,
a novoj klasi dodajte upotrebljivije metode.
712 Misliti na Javi

Kontejner WeakHashMap
Biblioteka kontejnera im a posebnu m apu za skladištenje slabih referenci: to je W eak-
HashMap. Ta klasa olakšava pravljenje kanonizovanih m apiranja (preslikavanja). Takvim
m apiranjem štedite m em oriju jer pravite sam o jednu instancu određene vrednosti. Kada
program u zatreba ta vrednost, on potraži postojeći objekat u tom m apiranju i upotrebi
njega (um esto da ga pravi od nule). M apiranje m ože napraviti vrednosti u sklopu svoje
inicijalizacije, ali se vrednosti češće prave tek na zahtev.
Pošto je ovo tehnika za uštedu m em orije, baš je podesno što WeakHashMap om o-
gućuje skupljaču smeća da autom atski čisti njene ključeve i vrednosti. S ključevima i
vrednostim a koje želite da smestite u m apu WeakHashMap ne m orate da radite ništa po-
sebno; sam a m apa ih autom atski om otava u objekte tipa WeakReference. O kidač koji
dozvoljava čišćenje jeste činjenica da se ključ više 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; / / Sačuvaj 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 ( ), pošto se upotrebljava


kao ključ u struktu ri podataka s transform isanjem ključeva. M etoda h a s h C o d e () opisana
je u preth o d n o m delu poglavlja.
Kada pokrenete program , videčete da skupljač smeča preskače svaki treči ključ, zato
što su u niz kljucevi smeštene i obične reference tih ključeva, te se ti objekti ne mogu
sakupljati kao smeče.

Kontejneri Jave 1.0/1.1


Nažalost, m nogo koda je napisano uz korišćenje kontejnera iz Jave 1.0/1.1, a ponekad se
ćak i nov kod piše uz korišćenje ovih klasa. Zbog toga m orate znati sve o starim kontej-
nerim a, m ada nikada ne bi trebalo da ih koristite dok pišete nov kod. M eđutim , stari kon-
tejneri su bili prilično ograničeni, pa o njim a nem a m nogo toga da se kaže. (Pošto su oni
sad prošlost, uzdržaću se od ismevanja nekih ishitrenih odluka prim enjenih pri njihovom
projektovanju.)

Vector i Enumeration
Jedini niz koji je mogao da se proširuje u Javi 1.0/1.1 bio je Vector,pa se dosta koristio. Nje-
govi nedostaci su suviše brojni da bi se ovde naveli (potražite 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 prilagođen 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 poboljšana, a ona u stvari postoji samo da bi podržavala stari Java kod.
Verzija iteratora u Javi 1.0/1.1 drugačije se zvala - Enum eration - tj. nije korišćen ter-
min koji je svima poznat. Interfejs Enumeration je m anji od interfejsa Iterator i sadrži
sam o dve m etode s dugačkim imenima: boolean hasMoreElements( ) koja vraća true
ako kontejner sadrži još elemenata, i Object nextElement( ) koja vraća sledeći element
kolekcije ako on postoji (inače generiše izuzetak).
Enumeration je samo interfejs bez realizacije, pa ga čak i nove biblioteke ponekad ko-
riste, što nije preporučljivo 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 oću m etode C o Ile ctio n s.e n u m eratio n () možete 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 ožete 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 ože-
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 posvećenim upoređivanju perform ansi, klasa
Hashtable je veoma slična 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 objašnjen 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 (nasleđena) iz kla-
se Vector. Zato im a sva obeležja i ponašanje kiase Vector, uz dodatne funkcije steka. Teško
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 puštanja u distribuciju, pa se i tako loš dizajn
jo š uvek sreće (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 ulanćanu 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 () + " " ) ;

/ / Ulančana 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 generišu se odgovarajući 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 takođe se izvode na ob-
jektu klase Stack. To je m oguće zato što je, zahvaljujući nasleđivanju, stek jedna vrsta vek-
tora. Stoga se sve operacije koje se m ogu obaviti s vektorom m ogu takođe obaviti i sa
stekom, npr. elem entA t().
Kao što je ranije pom enuto, kada želite ponašanje 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 kolićine inform acija tipa u k lju č e n o -is-
ključeno. O na je efikasna sam o s tačke gledišta veličine; ako vam je potreban brz pristup,
treba da znate da je ova klasa nešto sporija od nizova prirodnog tipa.
Osim toga, m inim alna veličina skupa bitova je long, tj. 64 bita. To znači da klasa BitSet
neće biti efikasna za čuvanje kraćih podataka, npr. onih od 8 bitova. U tom slučaju, bolje
je da napravite sopstvenu klasu, ili sam o niz u koji ćete sm estiti podatke ako vam je bitna
veličina. (To će biti slučaj sam o ako pravite m nogo objekata koji sadrže liste inform acija
tipa uključeno-isključeno, a odluku treba doneti tek na osnovu rezultata program a z.a op-
timizaciju i ostalih m erenja. Ukoliko ste to odlućili sam o zato što ste sami nešto proglasili
za preveliko, imaćete veom a složen program i izgubićete m nogo vrem ena.)
O bičan kontejner se širi kada m u đodajete nove elem ente, a to radi i BitSet. Sledeći
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 slučajnih brojeva koristi se za generisanje slučajnih brojeva tipa byte, sh o rt


i int, a svaki od njih se pretvara u odgovarajuču bit m asku u klasi BitSet. Ovo lepo radi
zato što je BitSet 64-bitni, pa nijedan od ovih brojeva ne prouzrokuje povečavanje njego-
ve dužine. Potom se pravi skup bitova veće dužine. Vidite da se BitSet širi po potrebi.
Ukoliko imate neprom enljiv skup indikatora koje možete da imenujete, obično je bolje
koristiti klasu EnumSet (videti poglavlje N abrojani tipovi ) nego klasu BitSet pošto Enum-
Set omogućuje rukovanje im enim a, a ne lokađjam a num eričkih bitova, i tim e smanjuje
količinu grešaka. EnumSet vas sprečava i da nehotice dodate nove lokacije indikatora, što
bi moglo da prouzrokuje ozbiljne greške koje se teško pronalaze. Koristite klasu BitSet
um esto klase EnumSet samo u slučaju da sve do trenutka izvršavanja program a ne znate
broj indikatora ili ako nije praktično dodeliti im im ena ili vam treba neka od specijalnih
operacija klase BitSet (pročitajte JDK dokum entaciju klasa BitSet i EnumSet).

Sažetak
Ima onih koji tvrde da je biblioteka kontejnera najvažnija biblioteka objektno orijentisanog
jezika. U većini programa kontejneri se upotrebljavaju više od ijedne druge kom ponente
biblioteke. Neki jezici (Python, na prim er) imaju ugrađene čak i osnovne kontejnerske
kom ponente (liste, mape i skupove).
Kao što ste videli u poglavlju Č uvanje objekata, pom oću kontejnera se može uraditi
m nogo toga zanimljivog, a bez m nogo napora. M eđutim , doći će trenutak kada ćete m o-
rati da znate više o kontejnerim a kako biste mogli pravilno da ih upotrebljavate - kon-
kretno, m orate znati dovoljno o operacijam a transform isanja ključeva (heširanja) 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 prilično dobro priprem ljeni za
korišćenje Java kontejnera u svakodnevnim program erskim zadacima.
Biblioteku kontejnera je teško projektovati (to važi za većinu dizajnerskih problem a s
bibliotekama). U C ++-u, kontejnerske klase su obuhvatale m nogo različitih klasa. ’I'o je
bilo bolje od onoga što je postojalo pre kontejnerskih klasa jezika C ++ (a to je ništa), 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 održava neku ravnotežu: ima svu funkcionalnost koju
očekujete od zrele biblioteke kontejnera, ali ju je lakše naučiti i lakše se koristi od kontej-
nerskih klasa jezika C++ i ostalih sličnih biblioteka kontejnera. Rezultat ponegde izgleda
čudno. Za razliku od nekih drugih odluka sprovedenih u starim Java bibliotekama, te čud-
novatosti nisu nastale slučajno, nego su posledica odluka pažljivo odvaganih kako bi se po-
stigao kom prom is po pitanju složenosti.
R ešenja o d a b ra n ih vežbi 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 težih zadataka za projektanta je-
zika. Posledica toga je veliki broj različitih 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 ožete da radite (datoteka, konzola, m re-
žne veze itd.), već im a i raznih načina za to (sekvencijalni, nasum ičan pristup, baferi, bi-
narni, znakovni, po redovim a, po rečima itd.).
Projektanti Javinih biblioteka pristupili su rešavanju 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 može da izgleda zastrašujuće (ironično, ali projekat Javinog U /I sistema zapravo
sprečava 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-
boljšanja perform ansi i funkcionalnosti. Usled toga postoji prilično veliki broj klasa koje
treba savladati da bi se stekla dovoljno dobra osnovna znanja o Javinom U/I sistemu. Pri-
lično je važno poznavati istorijat razvoja U/I biblioteke, čak i ako je vaša prva reakcija: ,,Ne
gnjavi me istorijom, već mi pokaži kako se koristi"! Problem je to što će vas bez predzna-
nja iz istorije ubrzo zbuniti neke klase i nećete znati ni kada da ih koristite, ni kada ne.
Ovo poglavlje je uvod u razne U/I klase u stanđardnoj Javinoj biblioteci i u način nji-
hovog korišćenja.

Klasa File
Pre nego što se pozabavim o klasama koje i čitaju i upisuju podatke u tokove, proučićem o
uslužne klase koje postoje u biblioteci, a pom ažu u radu s datotekam a.
Klasa File ima zbunjujuće ime: možete se prevariti i pomisliti da se odnosi na datoteku.
Bolje ime za klasu bilo bi FilePath. O na može da predstavlja !»;eodređenedatoteke ili im ena
skupa datoteka u direktorijum u. Ako je reč o skupu datoteka, možete da ga potražite meto-
dom list( ) koja vraća niz podataka tipa String. Vraćanje niza um esto neke prilagodljive
kontejnerske klase opravdano je zato što je broj elemenata nepromenljiv, a ako želite dru-
gačiji spisak sađržaja direktorijum a, treba da napravite drugačiji objekat klase File. U ovom
odeljku prikazuje se korišćenje ove klase i njoj pridruženog interfejsa FilenameFilter.

Listanje direktorijuma
Pretpostavićem o kako hoćete da vidite spisak sadržaja direktorijum a. Objekat klase File
možete upotrebiti na dva načina. Ako pozovete m etodu lis t ( ) bez argum enata, dobićete
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 pažnju na to da se rezultati veom a lako uređuju po abe-
cednom redosledu pom oću 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 pomoću 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 ( ) pružila 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 nađu 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), pošto li s t ( ) realizuje tek osnovnu
funkcionalnost, a vi dajete Strategy u obliku interfejsa F ilenam eFilter koji dovršava algo-
Poglavlje 18: Javin ulazno-izlazni sistem 721

ritam potreban metodi lis t() za pružanje usluge. Pošto je argument metode lis t() objekat
klase FilenameFilter, to znači da joj možete proslediti objekat bilo koje klase koja reali-
zuje interfejs FilenameFilter, i tako (čak i u vreme izvršavanja) odrediti način ponašanja
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 sadrži ime datoteke. Zapamtite da metoda lis t( ) poziva metodu
accep t() za svako ime datoteke u direktorijumu, da bi videla koje od njih treba uključiti
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
pomoču metode accept().

A n o n im n e unutrašnje klase
Ovaj primer je savršen za prepravljanje pomoću anonimne unutrašnje klase (takve klase
su opisane u poglavlju Unutrašnje klase). Kao prva promena, uvodi se metoda filter( )
koja vraća 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 pažnju na to da argument metode filte r() mora da bude označen kao final.
To je neophodno da bi anonimna unutrašnja klasa mogla da koristi objekat koji je izvan
njene oblasti važenja.
Ovakav pristup je bolji zato što je klasa koja realizuje interfejs FilenameFilter sada čvr-
sto povezana s klasom ListanjeDirektorijuma2. Međutim, ovaj pristup se može dodatno
poboljšati kada se definiše anonimna unutrašnja klasa kao argument metode Iist( ); u
tom slučaju, program je još kraći:

//: ui/ListanjeDirektorijuma3.java
// Pravljenje anonimne unutrašnje 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 označen kao final, pošto anonimna unutrašnja kla-
sa direktno koristi args.
Ovo pokazuje kako anonimne unutrašnje klase omogućuju pravljenje speđfičnih,
unikatnih klasa za brzo rešavanje problema. Prednost ovog pristupa jeste to što je kod koji
rešava odredeni problem izolovan na jednom mestu. S druge strane, kod nije uvek lako
čitljiv, pa morate mudro da ga koristite.
Vežba 1: (3) Izmenite program ListanjeDirektorijuma.java (ili neku od njegovih varija-
nata) tako da FilenameFilter otvara i čita svaku datoteku (uslužnom metodom net.m in-
dview.util.TextFile) i prihvata je na osnovu toga sadrži li ona bilo koji od argumenata
s komandne linije koji slede iza imena datoteke.
Poglavlje 18: Javin ulazno-izlazni sistem 723

Vežba 2: (2) Napravite klasu ListanjeUređenogDirektorijuma s konstruktorom koji pri-


ma objekat tipa File i od njegovih datoteka pravi uređenu 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).
Vežba 3: (3) Izmenite program ListanjeDirektorijuma.java (ili neku od njegovih varija-
nata) tako da sabere veličine izabranih datoteka.

Uslužne metode za direktorijume


U programiranju se često obavljaju operacije nad skupovima datoteka, bilo u lokalnom
direktorijumu bilo prolaskom kroz celo stablo direktorijuma. Dobro bi poslužila alatka
koja proizvodi taj skup datoteka. Sledeča uslužna ldasa proizvodi bilo niz File objekata u
lokalnom direktorijumu (metodom lo k a l()), bilo List<File> celog stabla direktorijuma
počev od datog direktorijuma (metodom p rolazak()). (File objekti su korisniji od imena
datoteka zato što sadrže više 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) { // Počni 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 početnog direktorijuma u objekat tipa File i poziva
rek u rzD irm e() koja obavlja rekurzivni prolazak kroz direktorijume, prikupljajući sve
Poglavlje 18: Javin ulazno-izlazni sistem 725

više informacija u svakoj rekurziji. Da bismo mogli da razlikujemo obične datoteke od di-
rektorijuma, povratna vrednost je zapravo ,,n-torka“ objekata - jedna lista koja sadrži
obične datoteke i druga koja sadrži direktorijume. Polja su namerno deklarisana kao jav-
na (public), pošto je svrha InfoStabla da prikupi objekte - da vračate samo jednu listu,
ne biste je napravili privatnom, pa ni vraćanje para objekata ne znači da ih morate napra-
viti privatnim. Obratite pažnju 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 lakše
pregledao. Podrazumevane metode to S trin g () za kontejnere, štampaju sve elemente jed-
nog kontejnera u istom redu. U velikim kolekcijama to postaje teško čitljivo, pa bi se mo-
glo poželeti i neko drugo formatiranje. Sledeća alatka dodaje prelaske u novi red
i uvlačenja 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 drugačije tretiraju posebni slučajevi kada
nema elemenata, odnosno kada je samo jedan element. Postoji i verzija metode p p r in t( )
za nizove.
Uslužne 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 uslužnih 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 počinju 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 počinju 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 sadrže "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
* ///:-

Možda je potrebno da osvežite 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 otišli korak dalje, napravićemo 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 ugnežden je u klasu O bradaD atoteka. Da biste ga realizovali, mo-


rate da implementirate O bradaD atoteka.Strategy i time čitaocu date veći kontekst.
O bradaD atoteka pronalazi datoteke koje imaju određeni nastavak (spolj kao argument
konstruktora) i predaje ih objektu Strategy (koji je takođe argument konstruktora).
728 Misliti na Javi

Ako ne zadate argumente, u klasi O bradaD atoteka pretpostavlja se da želite da prođe-


te kroz sve direktorijume koji izlaze iz tekućeg direktorijuma. S druge strane, možete
zadati određenu datoteku, s nastavkom ili bez njega(ona će dodati nastavak ako bude po-
trebno), odnosno jedan ili više direktorijuma.
U metodi m a in () vidite elementarni način upotrebe ove alatke; ona štampa imena svih
Java datoteka izvornog koda u skladu sa argumentima koje zadate na komandnoj linijii.
Vežba 4: (2) Upotrebite D irektorijum .prolazak() za sabiranje veličina svih datoteka u
stablu direktorijuma čija imena odgovaraju određenom regularnom izrazu.
Vežba 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 više od pukog prikaza postojeće datoteke ili direktorijuma. Objekat klase File
možete da koristite i za pravljenje novog direktorijuma ili celokupne putanje direktoriju-
ma ako ona ne postoji. Možete da ispitujete i karakteristike datoteka (veličinu, datum po-
slednje promene, mogućnost za čitanje/upisivanje), utvrđujete da li objekat klase File
predstavlja datoteku ili direktorijum i brišete datoteke. Slcdeći program prikazuje neke
od ostalih metoda klase File (potpun skup metoda potražite 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 može da se č i t a : " + f .c a n R e a d () +
" \ n može 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
može da se č i t a : t r u e
može 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() možete 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 premešta) datoteke na
potpuno novu putanju predstavljenu argumentom tipa File. Ovaj pristup se može kori-
stiti i za direktorijume proizvoljne dužine.
Ako budete eksperimentisali s gornjim programom, ustanovićete da možete napraviti
putanju direktorijuma proizvoljne složenosti zato što će m kdirs( ) obaviti sav posao
umesto vas.
Vežba 6: (5) Iskoristite program O bradaD atoteka i pronađite u nekom podstablu direk-
torijuma sve datoteke Java izvornog koda koje su promenjene nakon određenog 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 dešava s podacima unutar stvarnog U/I
uređaja.
Klase Javine biblioteke za U/I podeljene su na ulazne i izlazne, što možete videti u hi-
jerarhiji Javinih klasa u dokumentaciji na Webu. Zbog nasleđivanja, sve klase izvedene iz
InputStream ili Reader imaju osnovnu metodu re a d () za čitanje jednogbajta ili niza baj-
tova. Slično, sve klase izvedene iz O utputStream ili W riter imaju osnovnu metodu write(
) za upisivanje jednog bajta ili niza bajtova. Međutim, obično nećete koristiti te metode,
jer one postoje da bi ih koristile druge klase koje obezbeđuju korisnije metode. Retko ćete
praviti objekat toka korišćenjem samo jedne klase; umesto toga, grupisaćete više objekata
da biste postigli željenu funkcionalnost. Mogućnost da se napravi više 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, izvođe iz klase In-
putStream , a da sve klase povezane sa izlazom nasleđuju klasu OutputStream .
Kao i dosad u knjizi, pokušaću da prikažem klase, ali ću podrazumevati da ćete sve de-
talje, npr. kompletne spiskove metoda, tražiti u dokumentaciji na Webu.

Vrste ulaznih tokova


Klasa InputStream treba da predstavlja ulazne tokove iz različitih 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 određenom potklasom klase InputStream.
Pored toga, FilterlnputStream takođe je vrsta ulaznog toka koji služi kao osnova za do-
punske klase, tj. za dodeljivanje atributa ili korisnih interfejsa ulaznim tokovima. O ovo-
me će biti reči u nastavku.

Tabela U/l-1 Tipovi ulaznih tokova


Klasa Funkcija Argum enti konstruktora
Kako se koristi
ByteArraylnput- Omogućuje da se memorijski Bafer iz kojeg se izvlače bajtovi.
Stream blok koristi kao ulazni tok
Kao Izvor podataka. Povežite 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. Povežiteje 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. Povežite je sa objektom
klase FilterlnputStream da biste dobili kori-
stan interfejs.
Pipedlnput Daje podatke koji se upisuju PipedOutputStream
Stream u pridruženi PipedOutput-
Kao izvor podataka u višenitnom radu. Pove-
Stream. Realizuje slanje kroz
žiteje sa objektom klase FilterlnputStream
cevovod. da biste dobili koristan interfejs.
Sequencelnput- Konvertuje dva ili više objekata Dva objekta klase InputStream ili Enume-
Stream klase InputStream ujedan ula- ration za kontejner objekata klase Input-
zm tok. Stream
Kao izvor podataka. Povežite 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 odlučuju gde će se usmeriti izlaz: u niz bajtova (ali ne
i String; znakovni niz možete da napravite korišćenjem niza bajtova), datoteku ili cev.
Pored toga, FilterO utputStream služi kao osnovna klasa za dopunske klase koje do-
daju atribute ili korisne interfejse u izlazne tokove. O ovome će biti reči 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 početna veličina bafera.
O utputStream koje šaljete u tok smeštaju se u taj
Za zadavanje odredišta podataka.
bafer.
Povežite 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 odredišta podataka.


Povežite je sa objektom klase Filter-
OutputStream da biste dobili korr
stan interfejs.
PipedO utput- Sve informacije koje upisujete PipedlnputStream
Stream u ovaj tok automatski završavaju
Za zadavanje odredišta podataka
kao ulaz za odgovarajući Piped-
u višenitnom radu. Povežiteje 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 Generički 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 može i da ga proširi, što se dešava u nekim filtarskim klasama.
Ovakav obrazac, ipak, ima i nedostatak. Projektni obrasci Decorator omogućuju mnogo
veću prilagodljivost programa (pošto se atributi lako kombinuju), ali povećavaju 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, obezbeđuju dekoratorski interfejs za kontrolisanje određenog 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 ključni zahtev dekoratora (da
bi obezbedio zajednički interfejs za sve omotane objekte).

Filtriranje ulaznog toka


Klase koje realizuju FilterlnputStream izvršavaju dva prilično različita zadatka. D ataln-
putStream omogučuje čitanje različitih prostih tipova, kao i objekata klase String - za to
služe metode koje počinju rečju read (čitanje), npr. readB yte(), readFIoat() itd. Ova kla-
sa, uz srodnu klasu D ataO utputStream , omogučuje premeštanje prostih tipova podata-
ka s jednog mesta na drugo pomoću toka. Ta mesta određuju klase iz tabele U /I-l.
Preostale klase menjaju način internog ponašanja ulaznog toka: određuju da li je on
baferisan ili nije, da li prati broj redova koje čita (i omogućuje da zatražite red po broju ili
da zadate broj reda) i da li se jedan znak može vratiti u bafer. Poslednje dve klase prilično
liče na podršku za pravljenje prevodioca (verovatno su dodate da bi se podržao eksperi-
mentalni „Javin prevodilac pisan na Javi“), pa ih verovatno nećete koristiti u svakodnev-
nom programiranju.
Gotovo uvek morate da baferišete ulaz, bez obzira na to s kojim U/I uređajem ste po-
vezani, pa bi bilo korisnije da U/I biblioteka ima specijalan slučaj (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
Sadrži kompletan interfejs koji omoguču-
toka na prenosiv način čitali prosti
je čitanje prostih tipova.
tipovi (int, char, long itd.).
Bufferedlnput- Koristite je za smanjenje InputStream, uz opcionu veličinu bafe-
Stream broja fizičkih operacija ra.
čitanja svaki put kada vam zatreba
Ne obezbeduje interfejs sama po sebi,
još podataka. Ovom klasom ka-
već samo procesu dodaje bafer. Pridruži-
žete toku: „Upotrebi bafer".
te jo j objekat koji realizuje interfejs.
LineNumber- Prati brojeve redova u ulaznom InputStream
InputStream toku: možete da pozovete get-
Samo dodaje numerisanje redova. pa
LineNum ber( ) i setLinelMum-
čete jo j verovatno pridružiti objekat koji
ber(int)
realizuje interfejs.
Pushbacklnput- Ima skladište za vraćanje jednog InputStream
Stream bajta u koji možete da smestite
Obično se koristi u analizatoru za prevo-
posleđnji pročitani znak.
dioca. Verovatno je nećete koristiti.
734 Misliti na Javi

Filtriranje izlaznog toka


Odgovarajuća klasa za D atalnputStream je D ataO utputStream ; ona formatira sve pro-
ste tipove i objekte klase String u tok na takav način da ih može čitati svaki tok tipa Da-
talnputStream na bilo kojoj platformi. Sve njene metode počinju rečju 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 može prikazivati ljudima. To se razlikuje od D ataO utput-
Stream, čiji je cilj da elemente podataka postavi u binarni niz na takav način da ih Data-
InputStream može rekonstruisati nakon prenosa.
Dve važne metode klase PrintStream su p r in t( ) i p r in tln () koje se redefinišu za ispis
svih tipova. Razlika između ove dve metode je to što p rin tln ( ) prelazi u novi red po za-
vršetku štampanja.
Klasa PrintStream može da bude problematična 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
način nezavisan od platforme (ti problemi su rešeni pomoću klase PrintW riter o kojoj će
biti reči kasnije.
BufferedOutputStream je đopunska klasa koja nalaže toku da koristi baferisanje da se
podaci ne bi fizički 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
način.
PrintStream Koristi se za formatiranje izlaza. OutputStream, uz opcioni argument
Za razliku od klase D ataO u tp u t- tipa boolean koji određuje da li se
Stream koja skladišti podatke, Print- bafer prazni prilikom prelaska u sleđeči
Stream ih ispisuje. red.
Trebalo bi da bude „konačno" omotava-
nje za objekat kiase OutputStream
Verovatno ćeteje dosta koristiti.
Buffered- Koristite je da biste izbegli flzičko upi- O utputStream, uz opcionu veličinu
O utputStream sivanje pri svakom slanju podataka. bafera.
Ovom klasom kažete toku da „korist, ba- Ne obezbeduje mterfejs sama po seb,
fer ■Za pražnjenje sadržaja 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 značajno 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, dobićete upozorenje od prevodioca), klase InputStream i O utputStream i dalje
obezbeduju važne 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 očigled-
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 posredničke 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 podržava samo osmobitne binarne tokove, odnosno ne radi dobro sa še-
snaestobitnim Unicode znakovima. Pošto 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 podržao standard Unicode u svim U/I operacijama. Pored toga, projekto-
vane su nove biblioteke koje rade brže nego stare.

Izvori i ponori podataka


Za gotovo sve originalne ulazno/izlazne Javine klase postoje ekvivalentne potklase Reader
i W riter koje obezbeđuju osnovni rad sa Unicode znakovima. Međutim, postoje prilike
kada su binarni ulazni i izlazni tokovi ispravno rešenje. To se naročito odnosi na biblioteke
java.util.zip koje su binarno, a ne znakovno orijentisane. Zbog toga je najmudrije pokušati
koristiti klase Reader i W riter kad god je to moguće, a situacije kada morate da koristite bi-
narno orijentisane biblioteke otkrićete sami zato što kod neće moći da se prevede.
Evo tabele koja prikazuje ve/.u između izvora i ponora informacija (tj. mesta s kojih
podaci fizički stižu i kuda odlaze) u dve hijerarhije.

Izvori i ponori: klasa Jave 1.0 O dgovarajuća klasa Jave 1. 1


InputStream Reader
Konvertor InputStream Reader
OutputStream Wnter
Konvertor OutputStream\X/riter
FilelnputStream FileReader
FileOutputStream FileU/riter
StringBufferlnputStream StringReader
(zastarelo)
|nema odgovarajuće klase) StringW riter
ByteArraylnputStream CharArrayReader
ByteArrayOutputStream CharArrayW riter

PipedlnputStream PipedReader
PipedOutputStream PipedW riter

U većini slučajeva otkrićete da su interfejsi dve različite hijerarhije slični, ako ne i istovetni.
736 Misliti na Javi

Menjanje ponašanja toka


U klasama InputStream i O utputStream , tokovi su prilagođeni specifičnim potrebama
pomoću 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 način.
U sledećoj 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 proširenja, ili samo da se
ne biste pitali zašto je nema). Međutim, interfejsi klasa su veoma slični.

Filtri: klasa Jave 1.0 O dgovarajuća 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 slučaju 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 više ne bi tre-
balo da radite pomoću klase D atalnputStream (o tome ćete dobiti upozorenje tokom
prevođenja), već pomoću klase BufferedReader. U svim ostalim slučajevima, D atalnput-
Stream se i dalje preporučuje.
Da bi se olakšao prelazak na korišćenje klase PrintW riter, njeni konstruktori prihva-
taju sve objekte tipa O utputStream , ali i Writer. Medutim, PrintW riter ne omogućuje
ništa bolje formatiranje od klase PrintStream ; njihovi interfejsi su doslovno isti.
Konstruktor klase PrintW riter takode ima opciju za automatsko pražnjenje internih
bafera, što se dešava 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:
Naročito se bez ikakvih izmena koristi klasa D ataO utputStream , pa za čuvanje i pro-
nalaženje podataka u prenosivom formatu možete da primenite hijerarhije InputStream
i O utputStream .
Poglavlje 18: Javin ulazno-izlazni sistem 737

Klase Jave 1.0 bez odgovarajučih klasa u Javi 1.1


DataO utputS tream
File
RandomAccessFile
SeguencelnputStream

Poseban slučaj: klasa RandomAccessFile


Klasa RandomAccessFiie koristi se za datoteke sa zapisima poznate veličine, pa s jednog
zapisa na drugi možete da se pomerate metodom seek(), a zatim da čitate ili menjate za-
pise. Zapisi ne moraju da budu iste veličine; treba samo da bude moguće odrediti njihovu
veličinu i mesto u datoteci u kojoj se nalaze.
Isprva je pomalo teško poverovati da RandomAccessFile nije deo hijerarhija klasa In-
putStream ili O utputStream . Međutim, 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 postojećih klasa InputStream ili Out-
putStream - to je sasvim posebna klasa, napisana kompletno od početka, sa sopstvenim,
većinom osnovnim, metodama. RandomAccessFile nije deo hijerarhije zato što se pona-
ša sasvim drugačije od ostalih U/I tipova: omogućuje kretanje unapred i unazađ po dato-
teci. U svakom slučaju, 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 pronalaženje pozicije unutar datoteke,
seek () za prelazak na novu poziciju unutar datoteke i le n g th () za određivanje maksimal-
ne veličine 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 nasumičnom čita-
nju (r) ili o čitanju i upisivanju (rw). Nema podrške za datoteke u koje može 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 pretraživanje dostupne su samo unutar klase RanđomAccessFile koja radi
iskljućivo s datotekama. Klasa BufferedlnputStream omogućuje označavanje pozicije
(čija se vrednost čuva u internoj promenljivoj) metodom m a rk () i vraćanje na tu poziciju
metodom re se t(), ali su te metode ograničene i nisu naročito korisne.
Od Jave 1.4, umesto većine, 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.

Tipične primene U/l tokova


lako klase U/I tokova možete da kombinujete na razne načine, verovatno ćete upotreblja-
vati samo nekoliko kombinacija. Sledeći primer se može koristiti kao osnovna referenca;
on prikazuje pravljenje i korišćenje tipičnih U/I konfiguracija.
U ovim primerima, obradu izuzetaka pojednostavićemo tako što ćemo prosleđiti izu-
zetke na konzolu, ali to je prikladno samo u malim primerima i uslužnim 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 povećala brzina, ta datoteka bi trebalo
da bude baferisana, pa dobijenu referencu prosledite konstruktoru klase BufferedReader.
Pošto ta klasa ima i metodu readL ine(), to je konačan objekat i interfejs iz koga ćete čitati.
Kada stignete do kraja datoteke, metoda read L in e() vraća 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 sadržaja datoteke


(uključujući i znakove za prelazak u novi red koji se moraju dodati pošto ih readL ine()
odseca). Na kraju se poziva metoda c lo se() za zatvaranje đatoteke.
Vežba 7: (2) Otvorite tekstualnu datoteku tako da je možete čitati red po red. Pročitajte
svaki red kao znakovni niz i stavite ga u listu tipa LinkedList. Ispišite sve redove povezane
(ulančane) liste obrnutim redosledom.
Vežba 8: ( 0 Promenite vežbu 7 tako da se ime datoteke koju čitate prosleđuje kao argu-
ment s komandne linije.
Vežba 9: ( 1) Promenite vežbu 8 tako da pretvara sva slova redova teksta iz liste ArrayList
u velika i šalje rezultate u tok System.out.

M ctoda close( ) biće pozvana au to m atsk i to k o m izvršavanja 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 objašn jen o je da to ne rad i kako su pro jek tan ti Jave oćekivali (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

Vežba 10: (2) Promenite vežbu 8 tako da kao dodatne argumente s komandne linije pri-
hvata reči koje će tražiti u datoteci. Ispišite sve redove u kojima se reči pronađu.
Vežba 11: (2) U primeru unutrasnjeklase/UpravljanjeStaklenomBastom.java, Kontro-
lerStakleneBaste sadrži fiksiran skup događaja. Promenite program tako da čita događaje i
njihova relativna vremena iz tekstualne datoteke. (nivo težine 8): Za pravljenje događaja
upotrebite projektni obrazac Factory M ethod (Proizvodna metoda) pronaći ćete ga u knjizi
Thinking in Patterns (with Java) koja se može 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 pažnju na to da metoda re a d () vraća sledeći 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 pročitali formatirane podatke, upotrebite binarno orijentisanu U/1 klasu Dataln-
putStreani (a ne neku znakovno orijentisanu). To znači da morate koristiti klase hijerar-
hije InputStream , a ne klase hijerarhije Reader. Naravno, pomoću klasa tipa InputStream
možete da pročitate 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-
jući ulazni tok koji ćete proslediti klasi D atalnputStream .
Ako ćitate znakove iz toka tipa D atalnputStream bajt po bajt pomoću metode read-
B y te(), svaka vrednost može biti važeći rezultat, pa se povratna vrednost ne može kori-
stiti za otkrivanje kraja ulaznih podataka. Umesto toga možete 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 pažnju na to da metoda available() radi različito, u zavisnosti od toga koja se


vrsta medija čita; u opštem slučaju, ona vraća broj bajtova koji se mogu pročitati bez blo-
kiranja. Ako se radi o datoteci, to podrazumeva celu datoteku, ali za neku drugu vrstu
toka možda ima drugačije značenje, pa ovu metodu pažljivo koristite.
Kraj ulaznih podataka u slučajevima poput ovog mogli biste da otkrijete i hvatanjem izu-
zetka. Međutim, korišćenje 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 baferiše omotavanjem u BufferedW riter (pokušajte da izostavite omotavanje da bi-
ste videli kakav će to uticaj imati na performanse: baferisanje neverovatno poboljšava
performanse U/I operacija). U ovom primeru, izlazni tok se radi formatiranja dekoriše
kao PrintW riter. Datoteka koja je napravljena na ovaj način može se čitati kao obična
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, uvećava se ukupan broj redova. Obratite pažnju na
to da nisam upotrebio klasu LineN um berlnputStream , pošto je sasvim beskorisna. Kao
što je ovde pokazano, veoma je lako pratiti ukupan broj redova.
Kada se ulazni tok iscrpe, metoda readLine( ) vraća null. Videćete eksplicitan poziv
metode cIose( ) za datoteku out. Ako ne pozovete metodu close( ) za izlaznu datoteku,
može se desiti da bateri ne budu ispražnjeni i da se zato izgube podaci.

Prečica za pisanje u tekstualnu datoteku


U Javi SE5, klasi PrintW riter dodat je pomoćni konstruktor, da ne biste morali ručno da
radite đekorađju svaki put kada hoćete da napravite tekstualnu datoteku i nešto u nju
upišete. Izmenio sam program OsnovePisanjaUDatoteku.java tako da koristi ovu prečicu:

/ / : 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. Nažalost, za ostale uobiča-
jene programerske zadatke nisam napisao prečice, pa tipičan U/I i dalje zahteva mnogo
redundantnog teksta. Međutim, u ovoj knjizi se upotrebljava uslužna metoda TextFile
koja če biti definisana malo kasnije u ovom poglavlju - ona pojednostavljuje te uobičaje-
ne programerske zadatke.
Vežba 12: (3) Promenite vežbu 8 tako da se otvara tekstualna datoteka u koju tekst može
i da se upisuje. Upišite u datoteku redove koji se nalaze u ulančanoj listi (LinkedList) i nji-
hove brojeve. (Nemojte koristiti klase LineNumber.)
Vežba 13: (3) Promenite vežbu OsnovePisanjaUDatoteku.java tako da za praćenje broja
redova upotrebljava LineNumberReader. Obratite pažnju na to koliko je lakše pratiti taj
broj programski.
Vežba 14: (2) Na osnovu programa OsnovePisanjaUDatoteku.java, napišite program
koji poredi performanse upisivanja u datoteku pri korišćenju baferisanog i nebaferisanog
ulaza/izlaza.

Čuvanje i rekonstruisanje podataka


Klasa PrintVVriter formatira podatke u oblik koji čovek može da čita. Međutim, da bi se
podaci iskazali u obliku koji drugi tok može da prepozna, koristi se D ataO utputStream
za upisivanje, odnosno D atalnputStream za rekonstruisanje podataka. Naravno, ovi to-
kovi bi mogli da sadrže 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 moći taćno da rekonstruišete pomoću 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 brinući o prilagođavanju programa različitim platforma-
ma. Takav problem nestaje ako lava postoji na obe platforme.3
Kada koristite DataO utputStream , jedini naćin pisanja znakovnog niza (ol^jekta tipa
String) koji osigurava njegovu pouzdanu rekonstrukuju pomoću ulaznog toka D ataln-
putStream jeste kodiranje UTF-8 koje se u ovom primeru obavlja metodama writeUTF(
) i readU T F (). UTF-8 je višebajtni format čija se dužina kodiranih znakova menja u za-
visnosti od upotrebljenog skupa znakova. Ako radite (isključivo ili pretežno) sa ASCII
znakovima (koji zauzimaju samo sedam bitova), korišćenjem 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, dužina znakovnog niza se smešta u prva dva
bajta UTF-8 znakovnog niza. Međutim, 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, inače čitanje neće biti ispravno.
Kada koristite D ataO utputStream i metode w riteU T F() i readU TF(), možete mešati
znakovne nizove i ostale tipove podataka, jer će znakovni nizovi biti ispravno usldadišteni
kao Unicode i Iako će se rekonstruisati pomoću ulaznog toka D atalnputStream .
Metoda w riteD ouble() stavlja broj tipa double u tok, a rekonstruiše ga odgovarajuća
metoda readD ouble() - slične metode za čitanje i upisivanje postoje i za druge tipove. Da
bi metode za čitanje radile kako treba, morate znati tačnu poziciju podatka u toku, pošto
bi sačuvan podatak tipa double mogao da se pročita 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 omogućiti lakši način skladištenja i rekonstrukcije složenih 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

Vežba 15: (4) Pročitajte HTML đokum entaciju klasa D ataO utputStream i D atalnputSt-
ream na Webu. Na osnovu programa CuvanjelPronalazenjePodataka.java, napišite
program koji skladišti i zatim rekonstruiše sve moguće tipove s kojima rade klase Data-
O utputStream i D atalnputStream . Dokažite da se svi tipovi ispravno skladište i rekon-
struišu.

Čitanje i upisivanje datoteka s nasumičnim pristupom


Korišćenje klase RandomAccessFile je kao kombinovanje tokova D atalnputStream i
D ataO utputStream pošto realizuje jednake interfejse. Osim toga, za nesekvencijalno kre-
tanje po datoteci i promenu nasumično raspoređenih vrednosti na raspolaganju je meto-
da seek ().
Preduslovza korišćenje klase RandomAccessFile jeste da znate (možete da izračunate)
položaj svih entiteta u datoteci, jer ćete jedino tako moći ispravno s njima da radite. Ran-
domAccessFile ima specifčne 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. Pošto svaki broj tipa
double (uvek) zauzima osam bajtova, da biste metodom seek() pronašli šestu vrednost
po redu, morate izračunati 5*8 i dobićete poziciju njenog početnog 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-
država dekorisanje, pa je zbog toga ne možete kombinovati ni s jednim aspektom potkla-
sa InputStream i O utputStream . Morate pretpostaviti da je klasa RandomAccessFile
ispravno baferisana, pošto tu funkciju ne možete da joi dodate.
ledina opcija koja vam stoji na raspolaganju jeste drugi argument konstruktora; datote-
ku s nasumičnim pristupom možete da otvorite radi čitanja (r) ili čitanja i upisivanja (rw).
Imajte u vidu da umesto klase RandomAccessFile možete koristiti nio datoteke pres-
likane u memoriju.
Vežba 16: (2) Pročitajte HTM LdokumentacijuklaseRandomAccessFilenaW ebu. Na os-
novu programa KoriscenjeRandomAccessFile.java, napišite program koji smešta i zatim
rekonstruiše sve moguće tipove s kojima radi klasa RandomAccessFile. Dokažite da se svi
tipovi ispravno skladište i rekonstruišu.

Cevovodi
Klase PipedlnputStream , PipedO utputStream , PipedReader i PipedW riter pomenute
su u ovom poglavlju samo ukratko. Nemojte na osnovu toga zaključiti da nisu korisne.
Njihova korisnost nije očigledna dok ne počnete da isprobavate višenitni rad, jer se cevo-
vodi upotrebljavaju za komunikaciju između niti. O ovome će, uz primer, biti reči u po-
glavlju Paralelno izvršavanje.

Uslužne klase za čitanje i pisanje


U programiranju veoma često treba učitati 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 količina koda da bi se obavile te uobičajene operacije - u
njoj nema jednostavnih pomoćnih funkcija koje bi to radiie umesto nas. Da stvari budu
746 Misliti na Javi

još gore, zbog dekoratora je prilično teško zapamtiti kako se otvaraju datoteke. Dakle, bilo
bi dobro dodati biblioteci pomoćne klase koje će te elementarne operacije raditi umesto
nas. Java SE5 je klasi PrintW riter dodala prigodni konstruktor koji olakšava otvaranje
tekstualne datoteke za upisivanje. Međutim, 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 sadrži statične metode za čitanje i upi-
sivanje tekstualnih datoteka u obliku jednog znakovnog niza, a možete napraviti i Text-
FUe objekat koji redove te datoteke čuva u listi ArrayList (pa tokom rada sa sadržajem
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 pomoču 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);
}
/ / Obično 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 uređenoj 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 početnim 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, Obično, 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 vraća znakovni
niz koji sadrži celu datoteku. Metoda w rite () otvara tekstualni znakovni niz i upisuje ga
u datoteku.
Vodite računa o tome da svaki program koji otvara neku datoteku čuva njen poziv me-
tode clo se() unutar bloka finaUy, da bi datoteka bila zajemčeno zatvorena.
Konstruktor pretvara datoteku u znakovni niz pomoću metode re a d (), zatim poziva
S tring.split() da bi taj rezultat podelio na redove (uz znakove za prelazak u novi red kao
graničnike; ako ovu klasu često upotrebljavate, mogli biste da prepravite taj konstruktor
tako da bude delotvorniji). Nažalost, ne postoji odgovarajuća metoda ,,spajanja“ redova u
znakovni niz, pa nestatičnom m etodom w rite () moramo da ispisujemo red po red ručno.
Pošto 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. Međutim, možda ćete mo-
rati da napišete drugu verziju koja IOException izuzetke prosleđuje pozivaocu.
U metodi m a in () obavlja se elementarna provera ispravnosti rada spomenutih metoda.
Nije trebalo mnogo koda da bi se napisala ova uslužna klasa, a ona će nam uštedeti
mnogo vremena i olakšati život, u šta ćete se uveriti u nekima od narednih primera u
ovom poglavlju.
Problem s čitanjem tekstualnih datoteka može se rešiti i na drugi način, klasom ja-
va.util.Scanner uvedenom u Javu SE5. Medutim, ona služi 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“.
Vežba 17: (4) Pomoću klase TextFile i mape M ap<Character,Integer> napišite program
koji prebrojava znakove u datoteci. (Dakle, ako u nekoj datoteci ima 12 primeraka slova
a, 12 treba da sadrži objekat tipa Integer pridružen objektu tipa Character koji sađrži a u
mapi).
Vežba 18: (1) Izmenite program TextFile.java tako da izuzetke IOExceptions prosleduje
pozivaocu.

Čitanje binarnih datoteka


Slično 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


/ / Uslužna 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 vraćaju rezultujući niz tipa byte.
Metodom available() pravi se niz odgovarajuće veličine, a baš ova verzija preklopljene
metode re a d () popunjava taj niz.
Vežba 19: (2) Pomoću klase BinarnaDatoteka i mape Map<Byte,Integer> napišite pro-
gram koji prebrojava različite bajtove u datoteci.
Vežba 20: (4) Pomoću metode Direktorijum .prolazak( ) i klase BinarnaDatoteka, do-
kažite da sve .class datoteke u stablu direktorijuma počinju 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 označava jedin-
st\'en tok informacija koji koristi program. Svi ulazni podaci programa stižu iz
standardnog ulaznog toka, svi izlazni podaci šalju se na standardni izlazni tok, a sve poruke
0 greškama šalju se na standardni tokzagre'ske. Standardni ulazno/izlazni tokovi su znača-
jni zato što omogućuju lako povezivanje programa, a standardni izlazni tok jednog pro-
grama može da postane standardni ulazni tok drugog programa. To je snažna alatka.

Čitanje standardnog uiaznog toka


Oslanjajući 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 pomoću objekta Sy-
stem .out koji je već omotan kao objekat klase PrintStream. System.err na sličan način
biva objekat klase PrintStream , a System.in je tipa InputStream , bez omotavanja. To
znači sledeće: za razlikti od objekata System.out i System.err koje možete da koristite od-
mah, objekat System.in mora da se omota da biste nešto čitali iz njega.
Ulaz, po pravilu, čitate red po red, koristeći metodu readL ine(), pa System.in treba da
omotate u BufferedReader. Da biste to uradili, morate da konvertujete System.in u Rea-
der pomoću 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 završa 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() može
da generiše izuzetak IOException. Vodite računa o tome da System.in obično treba da
bude baferisan, kao i većina tokova.
Vežba 21: (1) Napišite program koji prima standardan ulaz, pretvara sva primljena
slova u velika, a zatim rezultate šalje na standardan izlaz. Preusmerite u ovaj program
sadržaj 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 možete 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
* ///-.-

Važno 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 omogućilo automatsko pražnjenje izlaznih
bafera, inače se izlaz neće prikazivati red po red.
Poglavlje 18: Javin ulazno-izlazni sistem 751

Preusmeravanje standardnog ulaza/izlaza


Javina klasa System omogućuje preusmeravanje standardnih ulaznih tokova, izlaznih to-
kova i tokova za greške, pozivanjem jednostavnih statičkih metoda:
setln(InputStream )
setO ut(PrintStream )
setErr(PrintStream )
Preusmeravanje izlaza je naročito korisno ako odjednom počnete da ispisujete veliku
količinu izlaznih podataka na konzolu, pa se oni kreću brže nego što možete da ih proči-
tate.4 Preusmeravanje ulaza je korisno za konzolne programe pom oću kojih želite više
puta da testirate određenu sekvencu ulaznih podataka. Evo jednostavnog prim era koji
prikazuje korišćenje 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 greške u drugu datoteku. Obratite pažnju na to da se na početku pro-
grama skladišti referenca prvobitnog System.out objekta i da se na kraju standarđni izlaz
vraća 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 Grafička korisnička okruženja 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 pokrećete druge programe operativnog sistema i da
upravljate ulazom i izlazom tih programa. Javina biblioteka ima klase za takve operacije.
Uobićajen zadatak je pokretanje programa i slanje rezultujućeg izlaza na konzolu.
U ovom odeljku napisaćemo uslužnu klasu koja će pojednostaviti taj zadatak.
U ovoj uslužnoj klasi mogu nastati dve vrste grešaka: uobičajene greške koje prouzro-
kuju izuzetke - za njih ćemo samo ponovo generisati RuntimeException - i greške pri
izvršavanju samog procesa. Te greške ć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 pokrećete 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 rezultujući 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 pozivajuće 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
izvršavanja. To se čini zato što objekat tipa InputStream možemo čitati.
Rezultati programa stižu red po red, pa ih čitamo metodom readLine(). Kod nas se re-
dovi samo ispisuju, ali biste mogli da ih hvatate i vraćate iz metode k o m an d a().
Greške u programu šalju se na standardni tok za greške i hvataju pozivanjem metode
getErrorStream ( ). Ako ima grešaka, one se ispisuju i generiše se izuzetak IzuzetakOS-
Izvrsenja da bi pozivajući program rešio 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-


poručuje uz JDK).
Vežba 22: (5) Izmenite program OSIzvrsenje.java tako da rezultate izvršavanja programa
vraća u obliku liste znakovnih nizova, umesto da ih ispisuje u standardni izlazni tok. Po-
kažite kako se koristi nova verzija te uslužne 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 pomoću 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 oseća i pri U/I operacijama sa datotekama, što je tema ovog
poglavlja, i pri mrežnom ulazu/izlazu, obradenom u knjizi Thinking in Enterprise Java.
754 Misliti na Javi

Ubrzanje je posledica korišćenja kanala i bfl/era-struktura koje su bliže načinu na koji


sam operativni sistem obavlja U/I operacije. Po analogiji s rudnikom uglja, kanal bi bio
rudnik koji sadrži žilu uglja (podatke), a bafer su kola koja se šalju u rudnik. Kola se
vraćaju 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 proćitate HTML dokumentaciju ldase java.nio.ByteBuffer,
videćete da je prilično elementarna: njenom konstruktoru treba saopštiti koliko memo-
rijskog prostora da rezerviše, i postoje metode za umetanje i vađenje podataka, bilo u si-
rovom obliku bajtova bilo u obliku prostih tipova. Ali nema načina da umetnete ili
izvadite objekat, pa čak ni znakovni niz. Sve je zadržano na prilično niskom nivou, baš
zato što se u većini 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
pažnju 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 uslužne metode koje od kanala proizvode objekte
tipova Reader i Writer.
Evo jednostavnog primera u kojem sve tri vrste tokova proizvode kanale u koje se
može 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 ( ) ) ; / / Pređi 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 prilično jednostavan: može m u se predati objekat tipa ByteBuffer radi či-
tanja ili pisanja, i delove datoteke možete zaključati, odnosno ostvariti isključivo pravo
pristupanja (to če biti opisano u nastavku).
Jedan od načina smeštanja bajtova u ByteBuffer jeste da ih neposredno umetnete u
njega nekom od put metoda, i tako upišete jedan ili više bajtova ili vrednosti prostih tipo-
va. Međutim, iz programa vidite da metodom w ra p () možete postojeći niz tipa byte da
omotate objektom tipa ByteBuffer. Kada to uradite, pripadajući niz se ne kopira, nego
koristi kao skladište za generisani ByteBuffer. Kažemo da je taj ByteBuffer poduprt ni-
zom.
Datoteku podaci.txt ponovo otvara klasa RandomAccessFile. Vodite računa o tome
da FileChannel možete premeštati po datoteci; ovde sam ga premestio na kraj, tako da se
novi upisi dodaju na kraj postojećih (engl. append).
Kada primenjujete pristup samo za čitanje, memorijski prostor za ByteBuffer morate
eksplicitno dodeliti statičnom metodom allo cate(). Svrha nio klasa je brzo premeštanje
velikih količina podataka, pa nije nevažno koju veličinu bafera ByteBuffer zadate - za-
pravo, ovde će vam 1 K verovatno mnogo manje značiti nego inače (moraćete da ekspe-
rimentišete s tekućom aplikacijom da biste odredili najbolju veličinu bafera).
U potrazi za još većom brzinom, mogli biste metodom allocateD irect() umesto me-
todom allocate() napraviti direktan bafer koji je još tešnje spregnut sa operativnim siste-
mom. Međutim, režijski troškovi takve dodele su veći, a njena realizacija se menja u
zavisnosti od operativnog sistema, pa ćete opet morati da eksperimentišete s tekućom ap-
likacijom da biste odredili donose li vam direktni baferi ikakvo ubrzanje.
Nakon što pozovete read( ) da biste kanalu FileChannel saopštili da upiše bajtove u
ByteBuffer, morate pozvati metodu flip( ) za taj bafer kako biste mu saopštili da se pri-
premi za vađenje bajtova iz njega. (To jeste malo nezgrapno, ali ne zaboravite da se radi o
veoma niskom nivou i da se tako postiže maksimalna brzina). A kada bismo taj bafer
upotrebili za još neku operaciju re a d (), pre svakog učitavanja sađržaja 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 pomoću 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 znači da smo đošli do kraja ulaza. Nakon svakog
poziva metode re a d () koja u bafer smešta 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 pokazivače 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 nešto 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, videćete da prilikom ispisivanja datoteke
podatke vadimo bajt po bajt, pa svaki byte zasebno pretvaramo u char. To je primitivno
- pročitajte dokumentaciju klase java.nio.CharBuffer i videćete da se u njoj za metodu
to S trin g () kaže: „Vraća znakovni niz koji sadrži znakove bafera“. Pošto se ByteBuffer na-
kon obrade metodom asC harB uffer() može smatrati baferom CharBuffer, zašto ne bis-
mo upotrebili njega? Kao što vidite iz prvog reda ispisa rezultata sledećeg programa, to
ipak ne radi kako smo očekivali:

/ / : 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 može 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());
/ / Pisaćemo 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 ) ; / / Više 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 sadrži sirove bajtove, i da bismo njih pretvorili u znakove, moramo ih kodirati
prilikom stavljanja unutra (da bi nešto značili kada ih izvadimo iz bafera) ili dekodirati
prilikom vađenja iz bafera. To se može postići 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, eućp,


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 početak podataka) i zatim za dekodiranje podataka metodom decode( )
upotrebite podrazumevanu šemu kodiranja te platforme, dobićete CharBuffer koji se
lepo ispisuje na konzoli. Za pronalaženje 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 dobićete objekat tipa Charset
kojim se taj znakovni niz može dekodirati.
Druga mogućnost je da (metodom encode()) kodirate po šemi kodiranja koja će dati
rezultat koji se može štampati nakon čitanja datoteke, kao što vidite u trećem 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
očekivani tekst.
Najzad, vidite šta se dešava ako pišete u ByteBuffer preko CharBuffer bafera (o tome
će još biti reči). Baferu ByteBuffer dodelili smo 24 bajta. Pošto 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.
Vežba 23: (6) Napravite i ispitajte uslužnu metodu koja štampa sadržaj CharBufferbafera
sve do mesta od kojega se njegovi znaci više ne mogu odštampati.

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 vađenje različitih vred-
nosti pomoću tih metoda:

/ / : u i / D a jP o d a t k e . ja v a
/ / Tumačenje b a jt o v a i z B y te B u ffe ra na r a z l i č i t e načine
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 dođele memorije objektu tipa ByteBuffer, proverili smo da li su njegove vred-
nosti automatski postale nula - i jesu. Pročitane su sve 1024 vrednosti (do kraja bafera
koji daje metoda lim it()), i sve su bile nula.
Vrednosti prostih tipova najlakše je umetnuti u ByteBuffer pomoču odgovarajučih
,,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. Među svim tim metodama, jedino je čudna metoda p u t( ) za ShortBuf-
fer koja zahteva eksplicitnu konverziju tipa (a ona odseca i menja rezultujuću 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“ omogućuje da ByteBuffer u pozadini posmatrate kroz prozor određenog
prostog tipa. ByteBuffer je i dalje jedino skladište koje ,,podupire“ taj prikaz, pa se sve iz-
mene koje napravite u prikazu odražavaju u izmenama podataka u ByteBuffer baferu.
Kao što ste videli u prethodnom primeru, na raspolaganju su vam pomoćne metode za
umetanje prostih tipova u ByteBuffer. Prikaz omogućuje 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 pomoću 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 pomoću 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 skladištenje niza celih brojeva. Naredni


pozivi metoda g e t() i put( ) neposredno pristupaju lokacijama celih brojeva u pripada-
jućem ByteBufferu. Imajte u vidu da je pristupanje apsolutnim lokacijama dostupno i za
proste tipove, preko neposrednog obraćanja ByteBufferu.
Kada se pripadajući ByteBuffer preko bafera prikaza popuni ceiim brojevima ili ne-
kim drugim prostim tipom vrednosti, onda se ByteBuffer može upisati neposredno u ka-
nal. lednako lako je i čitanje iz kanala i upotreba bafera prikaza za konverziju svega u
762 Misliti na Javi

određeni tip proste vrednosti. U narednom prim eru se ista sekvenca bajtova redom tu-
mači kao broj tipa short, int, float, long i double, tako što se proizvode odgovarajući
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 proizveđen omotavanjem osmobajtnog niza tipa byte koji se zatim pri-
kazuje putem bafera prikaza svih prostih tipova. U narednom dijagramu prikazana su
različita tumačenja istih bitova kada se čitaju iz različitih 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

Vežba 24: (1) Izmenite program IntBufferPrimer.java tako da se koriste brojevi tipa
double.

Endiani
Podaci se na raznim računarima skladište na različite načine. Na „big endian“ plat-
formama, najznačajniji bajt se smešta na najnižu memorijsku adresu, dok se na „little en-
dian“ platformama najznačajniji bajt smešta na najvišu memorijsku adresu. Kada
skladištite vrednost veću od jednog bajta, kao što su int, float itd., morate uzeti u obzir re-
dosled bajtova. ByteBuffer skladišti podatke u „big endian“ obliku, a tako se podaci uvek
šalju i preko mreže. Redosled smeštanja bajtova podataka u ByteBufferu možete prome-
niti metodom order( ), uz argument ByteOrder.BIG_ENDIAN ili ByteOr-
der.LITTLE_ENDIAN.
764 Misliti na Javi

Pogledajmo ByteBuffer koji sadrži ova dva bajta:

bl b2

Ako ovebajtove pročitate (protumačite) kao broj tipa short (ByteBuffer.asShortBuf-


fe r()), dobićete broj 97 (00000000 01100001), ali ukoliko pređete na redosled bajtova lit-
tle endian, dobićete 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 između 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 može pozvati metoda array( ) da bi prikazala sve
bajtove. Metoda a rra y () je neobavezna i možete je pozvati za bafere poduprte nizom; u
protivnom, dobićete U nsupportedO perationException.
Pomoću prikaza CharBuffer umeće se charA rray u ByteBuffer. Nakon prikazivanja
svih bajtova uverićete 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 pomoću bafera


U narednom dijagramu ilustrovani su odnosi između nio klasa, pa možete videti kako da
premeštate i konvertujete podatke. Na primer, ako niz tipa byte želite da upišete 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 upišete u
FileChannel.

Podupiruća mreža ili sistem datoteka Uslužne


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 oću ByteBaffera
ka kodiranom toku podataka
e n c o d e (C h a rB u ffe r)______
Učitajte š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()
đecođe(ByteBuffer) ^
iz kodiranog toka podataka
766 Misliti na Javi

Vodite računa o tome da je ByteBuffer jedini način za umetanje podataka u kanal i


vađenje podataka iz kanala, i da od ByteBuf¥era jedino možete napraviti samostalni bafer
nekog od prostih tipova, sami ili nekom od ,,as“ metoda. Dakle, bafer nekog od prostih
tipova ne možete pretvoriti u ByteBuffer. Međutim, pošto podatke prostih tipova možete
umetati u ByteBuffer i vaditi ih iz njega preko bafera prikaza, to i nije neko ograničenje.

Detaljno o baferima
Bafer se sastoji od podataka i četiri indeksa za pristupanje tim podacima i delotvoran rad
s njima: marker, položaj, kraj i kapacitet. Postoje metode za postavljanje vrednosti tih in-
deksa, za njihovo resetovanje i za ispitivanje njihovih vrednosti.

capacity() Vrača kapacitet bafera.


c le a r() Briše sadržaj bafera, indeksu potožaja zadaje vrednost nula, a indeksu kraja vred-
nost indeksa kapacitet. Ovom metodom čistite postojeći bafer.
flipl ) Indeksu kraja zadaje vrednost indeksa položaja. a indeksu položaja vrednost nula.
Ovom metodom se bafer priprema za čitanje nakon upisivanja podataka.
lim it| ) Vraća vrednost indeksa kraj.
lim it(int kraj) Zadaje vrednost indeksa kraj.
mark( ) Indeksu markerzaćaje vrednost indeksa položaja.
position( ) Vraća vrednost indeksa položaja.
positionfint Zadaje vrednost indeksa položaja.
pol)
rem ainingl ) Vraća (kraj - položaj).
hasRemai- Vraća true ako u baferu ima elemenata izmedu mesta na koja pokazuju indeksi po
ning( ) ložaja i kraja

Ove indekse ažuriraju metode koje umeću i vade podatke iz batera. Tako indeksi
odražavaju 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 može 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 ističe da je cilj uvek koristiti ByteBuffer, pošto 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 položaja pokazuje na prvi element bafera, a indeksi kapacitet i kraj na posiednji.
U metodi simetricnoPreturanjef ), petlja while iterira dok indeks položaja ne dođe do
vrednosti kraj. Indeks (tekućeg) položaja bafera se menja kada se pozovu relativne funk-
cije g e t( ) ili p u t( ) (pozivi bez argumenata). Možete pozivati i apsolutne metode g e t() i
p u t( ), sa indeksom položaja 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 vređnost indeksa položaja 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() smeštaju 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 položaj = 0 , a vrednost promenljive c l na mesto položaj = 1. Za to možemo upo-
trebiti apsolutnu metodu p u t ( ) ili indeksu položaja 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 sledeće iteracije petlje, indeksu marker se zadaje tekuća vrednost indeksa po-
ložaja:

\/ \/
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 položaja
pokazuje na kraj bafera. Kada ispišete sadržaj bafera, ispisuju se samo znakovi između po-
ložaja i kraja. Dakle, da biste ispisali ceo sadržaj bafera, indeksu položaja morate me-
todom rew in d () zadati vrednost poćetka 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 vraća u svoje prvobitno stanje.

Datoteke preslikane u memoriju


Datoteke preslikane u memoriju omogućuju pravljenje i menjanje datoteka koje su pre-
velike da bi cele bile smeštene u memoriju. Kada je datoteka delom preslikana u memo-
riju, možete se pretvarati kako je cela u memoriji i da joj možete pristupati tretirajući je
kao običan 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 pomoću 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 završeno");
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 obrađili i čitanje i pisanjc, počinjemo 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 pažnju na to da možete zađati početnu tačku i dužinu oblasti u datoteci koju želite da
preslikate; to znači da imate mogućnost 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 dugačka je 128 MB, pa verovatno pre-


vazilazi maksimalnu veličinu pojedinačne datoteke u memoriji koju OS vašeg računara
dozvoljava. Izgleda kao da je cela datoteka odjedanput dostupna zato što se u memoriju
smešta tek jedan njen deo, a ostatak se izmešta napolje. Na taj način možete menjati
sadržaj veoma velikih datoteka (do 2 GB). Imajte u vidu da su, radi optimizovanja per-
formansi, za preslikavanje datoteka u memoriju upotrebljene uslužne metode pripadnog
operativnog sistema.

Performanse
Iako su performanse ,,starog“ U/I sistema zasnovanog na tokovima poboljšane realizaci-
jom pomoču nio klasa, obično se još mnogo brže pristupa datotekama preslikanim u me-
moriju. U narednom programu upoređene su njihove performanse (na veoma
jednostavan način):

/ / : 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-
trašnjim 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 pažnju 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
značajan kada se uporedi sa stanjem pri koriščenju U/I sistema zasnovanih na toku.
Vežba 25: (6) Napravite eksperiment tako što čete u primerima iz ovog poglavlja naredbu
ByteBuffer.aIlocate( ) promeniti u ByteBuffer.aIlocateDirect( ). Pokažite razlike u per-
formansama, i proverite da li se prim etno menja trajanje pokretanja programa.
Vežba 26: (3) Izmenite program strings/JGrep.java tako da koristi Java nio datoteke
preslikane u memoriju po đelovima.

Zaključavanje datoteka
Zaključavanje datoteka omogućuje sinhronizovanje pristupa datoteci kao deljenom re-
sursu. Medutim, dve niti koje se takmiče za istu datoteku mogu biti u različitim JVM-ovi-
ma ili jedna može biti Java nit, a druga nit operativnog sistema. Zaključavanje datoteka
vide ostali procesi operativnog sistema zato što se zaključavanje Java datoteka direktno
preslikava u uslužnu metodu operativnog sistema za zaključavanje.
Poglavlje 18: Javin ulazno-izlazni sistem 773

Sledi jednostavan prim er zaključavanja 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 zaključavati, pošto su to po svojoj prirodi entiteti jednog procesa; po
pravilu se mrežna utičnica ne deli između dva procesa.) tryL ock() je neblokirajuća me-
toda. Ona će pokušati da prigrabi bravu za sebe, ali ako u tome ne uspe (kada neki drugi
proces već ima tu bravu, a ona nije deljena), vratiće 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 oslobađa
(prepušta drugima) metodom FileLock.release().
Može se zaključati 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 zaključava oblast (veličina - položaj). Trećim argumentom se zadaje da li je brava


deljena (engl. shared lock).
Ako se metoda za zaključavanje pozove bez argumenata, njen učinak se ne menja s
promenom veličine datoteke. Ukoliko se brava pribavi za oblast od indeksa položaj do in-
deksa položaj+veličina, a datoteka se poveća preko granice položaj+veličina, onda taj
povećani deo datoteke neće biti zaključan. Pozivi metoda za zaključavanje bez argumena-
ta, zaključavaju celu datoteku, čak i kada se ona poveća.
774 Misliti na Javi

Podršku za isključive (engl. exclusive lock) ili deljene brave mora dati operativni sistem.
Ukoliko operativni sistem ne podržava deljene brave, a primi zahtev da napravi jednu
takvu, biče upotrebljena isključiva brava umesto deljene. Vrstu brave (deljena ili isključi-
va) možete saznati kad pozovete metodu FileLock.isShared().

Zaključavanje delova preslikane datoteke


Kao što je već bilo rečeno, obično se preslikavaju veoma velike datoteke. Ponekad treba
zaključati delove takve velike datoteke, kako bi ostali procesi mogli da menjaju njene ne-
zaključane delove. Primera radi, tako nešto činimo s bazom podataka da bi bila dostupna
većem broju korisnika istovremeno.
Evo primera s dve niti, od kojih svaka zaključava određeni 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 odsečak
(engl. slice) koji če biti izmenjen, a u metodi r u n ( ) pribavlja se brava za kanal datoteke (ne
možete pribaviti bravu bafera, nego samo kanala). Metoda lo ck () poziva se veoma slično
pribavljanju zaključane niti objekta - sada imate „kritičnu sekciju“ sa isključivim pristu-
pom tom delu datoteke.5
Brave se otključavaju automatski kada JVM izađe iii kada se zatvori kanai za koji je
brava pribavljena, ali za otključavanje objekta tipa FileLock možete i eksplicitno da po-
zovete metodu release() kao što je urađeno u prethodnom programu.

Komprimovanje
Javina U/I biblioteka sadrži klase koje podržavaju čitanje i upisivanje tokova u kompri-
movanom formatu. Te klase treba omotati oko postojećih 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 između ova dva tipa možete koristiti klase In-
putStream Reader i OutputStreamReader.)

Klasa za kom prim ovanje Funkcija


CheckedlnputStream Metoda G etCheckSum f ) vraća kontrolm zbir za bilo koji objekat klase
InputStream (nesamo dekomprimovanje).
CheckedOutputStream Metoda G etCheckSum f ) vraća 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

Višc in fo rm a c ija o n itim a n a c i ćete u p o g la v lju Paralelno izvršavanje.


776 Misliti na Javi

Iako postoje mnogi algoritmi za komprimovanje, Zip i GZIP se verovatno najčešće ko-
riste. Zbog toga s komprimovanim podacima možete lako da radite pom oću 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 slučajeve kada treba kom-
primovati jedan tok podataka (a ne kontejner međusobno nesličnih 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 prilično 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 uobičajeno č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 većeg broja datoteka u formatu Zip


Biblioteka koja podržava format Zip mnogo je obimnija. Pomoću nje možete Iako da
komprimujete više datoteka, a postoji čak i posebna klasa koja olakšava postupak čitanja
Zip datoteke. Ova biblioteka koristi standardni format Zip, pa bez problema radi sa svim
alatkama koje se mogu preuzeti sa Interneta. Sledeći prim er liči na prethodni, ali može da
radi s proizvoljnim brojem argumenata s komandne linije. Osim toga, prikazuje i koriš-
ćenje Checksum klasa za izračunavanje i proveru kontrolnog zbira datoteke. Postoje dve
vrste kontrolnih zbirova: Adler32 (brži) i CRC32 (sporiji, ali nešto tačniji):

/ / : 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 važeći 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

Podršku za isključive (engl. exclusive lock) ili deljene brave mora dati operativni sistem.
Ukoliko operativni sistem ne podržava deljene brave, a primi zahtev da napravi jednu
takvu, biće upotrebljena isključiva brava umesto deljene. Vrstu brave (deljena ili isključi-
va) možete saznati kad pozovete metodu FileLock.isShared().

Zaključavanje delova preslikane datoteke


Kao što je već bilo rečeno, obično se preslikavaju veoma velike datoteke. Ponekad treba
zaključati delove takve velike datoteke, kako bi ostali procesi mogli da menjaju njene ne-
zaključane delove. Primera radi, tako nešto činimo s bazom podataka da bi bila dostupna
većem broju korisnika istovremeno.
Evo primera s dve niti, od kojih svaka zaključava određeni 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 odsečak
(engl. slice) koji će biti izmenjen, a u metodi r u n ( ) pribavlja se brava za kanal datoteke (ne
možete pribaviti bravu bafera, nego samo kanaia). Metoda Iock() poziva se veoma slično
pribavljanju zaključane niti objekta - sada imate „kritičnu sekciju' sa isključivim pristu-
pom tom delu datoteke.5
Brave se otključavaju automatski kada JVM izade ili kada se zatvori kanal za koji je
brava pribavljena, ali za otključavanje objekta tipa FileLock možete i ekspliđtno da po-
zovete metodu release() kao što je urađeno u prethodnom programu.

Komprimovanje
Javina U/I biblioteka sadrži klase koje podržavaju čitanje i upisivanje tokova u kompri-
movanom formatu. Te klase treba omotati oko postojećih 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 između ova dva tipa možete koristiti klase In-
putStream Reader i OutputStreamReader.)

Klasa za kom prim ovanje Funkcija


CheckedlnputStream Metoda G etCheckSum ( ) vraća kontrolni zbir za bilo koji objekat klase
InputStream (ne samo dekomprimovanje).
CheckedOutputStream Metoda GetCheckSum f ) vraća 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 iše in fo rm a c ija o n itim a n a ći ć ete u p o g la v lju Paralelno izvršavanje.


776 Misliti na Javi

Iako postoje mnogi algoritmi za komprimovanje, Zip i GZIP se verovatno najčešće ko-
riste. Zbog toga s komprimovanim podađm a možete lako da radite pomoću 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 slučajeve kada treba kom-
primovati jedan tok podataka (a ne kontejner međusobno nesličnih 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 prilično 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 uobičajeno č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 većeg broja datoteka u formatu Zip


Biblioteka koja podržava format Zip mnogo je obimnija. Pomoću nje možete lako da
komprimujete više datoteka, a postoji čak i posebna klasa koja olakšava postupak čitanja
Zip datoteke. Ova biblioteka koristi standardni format Zip, pa bez problema radi sa svim
alatkama koje se mogu preuzeti sa Interneta. Sledeči primer liči na prethodni, ali može da
radi s proizvoljnim brojem argumenata s komandne linije. Osim toga, prikazuje i koriš-
ćenje Checksum klasa za izračunavanje i proveru kontrolnog zbira datoteke. Postoje dve
vrste kontrolnih zbirova: Adler32 (brži) i CRC32 (sporiji, ali nešto tačniji):

/ / : 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 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 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 važeći 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 način 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 dođaje u arhivu. Takav objekat sadrži obim an interfejs koji om ogučuje čitanje i
zadavanje svih podataka o datom elem entu Zip datoteke; ime, kom prim ovana i nekom -
prim ovana veličina, datum , kontrolni zbir, dodatna polja podataka, komentar, rnetoda
kompresije i da li se radi o stavki direktorijum a. M edutim , Javina biblioteka ne podržava
uvođenje lozinke iako je form at Zip om ogućuje. Pored toga, mada klase C lieckedlnput-
Stream i CheckedOutputStream podržavaju oba tipa kontrolnih zbirova (Adler32 i
CRC32), klasa ZipEntry podržava sam o interfejs za kontrolni zbir tipa CRC. To ograniče-
nje nam eće form at Zip, ali zbog toga se ne može koristiti brži algoritam Adler32.
Klasa Z ipInputStream ima m etodu getN extE ntry( ) za raspakivanje arhive. Ta meto-
da vraća sledeći objekat klase ZipEntry ako on postoji. Postoji i kraća varijanta - proči-
tajte datoteku pom oću klase ZipFile koja im a m etodu en trie s( ), a ona vraća objekat tipa
Enum eration u kojem su nabrojani objekti klase ZipEntries.
Da biste pročitali kontrolni zbir, m orate na neki način da obezbedite pristup odgova-
rajućem objektu klase Checksum. Pri tom se zadržava referenca na objekte tipa Checke-
dO utputStream i CheckedlnputStream , ali m ožete da se oslonite i na referencu na
objekat tipa Checksum.
Z bunjujuća m etoda u Zip tokovim a je setC om m ent( ). Kao što je pokazano u gornjem
prim eru, prilikom upisivanja u datoteku m ožete da stavite komentar, ali ne postoji način
Poglavlje 18: Javin ulazno-izlazni sistem 779

da ga pronađete u klasi ZipInputStream . Komentari su p o tp u n o podržani sam o u klasi


ZipEntry, i to pojedinačno, po svakom unosu u datoteku.
N aravno, pri koriščenju klasa za kom prim ovanje GZIP ili Zip niste ograničeni na da-
toteke - m ožete da kom prim ujete bilo šta, uključujuči i podatke s m režnog priključka.

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, slično 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 ožete da sme-
stite audio datoteke, slike, ali i datoteke s klasama.
JAR arhive su naročito 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 određenog
apleta u jednu JAR arhivu, om ogućuje se njihovo istovrem eno preuzim anje sam o jednim
zahtevom serveru, a prenos je brži zbog kompresije. Svakom elem entu JAR datoteke m ože
se radi bezbednosti pridružiti 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 možete da napravite sami ili će um esto vas to uči-
niti program jar.) Više inform acija o manifest datotekam a pronaći ćete u dokum entaciji
razvojnog paketa.
Uslužni 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] odredište [manifest] ulaznedatoteke

O pcije su sam o zbirka slova (nisu neophodne crtice ili neki drugi pom oćni znaci). Ko-
risnici Unixa/Linuxa prim etiće sličnost s alatkom tar. Opcije su:

c Pravi novu praznu arhivu.


t Lista sadržaj.
X Dekomprimuje sve datoteke iz arhive.
x datoteka Dekomprimuje navedenu datoteku
f Kaže „Sađa ću ti dostaviti ime datoteke". Akoje ne koristite, ja r pretpostavlja da će ula-
zm pođaci stići iz standardnog ulaznog toka ili, ako pravi datoteku, da izlazne podatke
treba da pošalje na standardm izlazm tok.
m Kaže 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
možete da stavite u putanju klasa.)
M Ne pravi automatski manifest datoteku.

Ako spisak datoteka koje treba arhivirati sadrži 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 tipičnih načina za korišćenje program a jar. Sledeća kom anda pravi JAR ar-
hivu mojaJarDatoteka.jar koja sadrži sve datoteke s klasam a u tekućem direktorijum u,
kao i autom atski generisanu m anifest datoteku:

jar cf mojaJarDatoteka.jar *.class

Sledeća kom anda radi isto što i p reth o d n i prim er, ali uključuje i korisnički 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 sadržaj 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 način se svi


objedinjuju u datoteku mojaAplikacija.jar. Tokom izvršavanja 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 korišćenjem opcije o (nula), moći ć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 može da traži datoteke s klasama u bibliotekam a lib l.jar i lib2.jar.
Alatka jar nije toliko korisna kao uslužni program zip. Na prim er, ne m ožete da doda-
jete ili ažurirate datoteke u postojećoj JAR arhivi, tj. ovakve arhive m orate da pravite ,,od
nule“. Takode, datoteke ne možete da prem eštate u JAR arhivu i da ih nakon prem eštanja
brišete. M eđutim , JAR datoteku, koja je napravljena u jednoj platform i, moći će da pro-
čita alatka ja r na bilo kojoj drugoj platform i, što je problem koji ponekad muči uslužne
program e zip.
Kao što ćete videti u poglavlju Grafička korisnička okruženja , 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 može postojati kada program prestane da se izvršava. 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 sačuva svoje inform acije čak i kada se program ne izvršava. Tada bi ob-
jekat postojao i kada se program pokrene sledeći put, i sadržao bi iste inform acije koje je
imao i kada se program izvršavao pretho dn i put. Naravno, to m ožete postići 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 ogućuje pretvaranje svakog objekta koji realizuje in-
terfejs Serializable u niz bajtova iz kojeg se taj objekat kasnije može p o tp u n o rekonstru-
isati. To važi čak i u m režam a, što znači da m ehanizam serijalizovanja autom atski
kom penzuje razlike izm eđu operativnih sistema. Dakle, m ožete da napravite neki objekat
na računaru koji radi pod W indow som , serijalizujete ga i pošaljete preko mreže na Unix
računar gde će on biti pravilno rekonstruisan. Ne m orate da brinete o predstavljanju
objekata na različitim računarim a, redosledu bajtova niti o drugim detaljima.
Samo po sebi, serijalizovanje objekata je zanimljivo zato što om ogučuje realizovanje
lake trajnosti (engl. lightw eight persistence). Setite se da trajnost znači kako životni vek
objekta nije određen izvršavanjem program a, već objekat živi i izm eđ u izvršavanja pro-
grama. Efekat trajnosti možete da postignete upisivanjem serijalizovanog objekta na disk
i njegovim rekonstruisanjem pri ponovnom pozivanju program a. Epitet laka potiče otud
što ne m ožete da definišete objekat p om oću neke rezervisane reči za trajnost i da prepu-
stite sistem u da brine o detaljim a (iako će i to m ožda jednog dana biti m oguće). 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). Više inform acija o njoj potražite u knjizi T hin king in Enterprise Java,
koja se m ože preuzeti sa adrese w w w .M in d V iew .n et.
Serijalizovanje objekata je đodato u jezik radi podrške dvema važnim funkcijama. Ja-
vino daljinsko pozivanje m etoda (engl. reniote m eth o d invocation, R M I) om ogućuje objek-
tim a na drugim računarim a da se ponašaju kao da se nalaze na vašem računaru. Pri slanju
poruka udaljenim objektim a, serijalizovanje je neophodno da bi se preneli argum enti i
povratne vrednosti. Daljinsko pozivanje m etoda objašnjeno 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 reči u poglavlju
Grafička korisnička okruženja. Kada se koristi zrno, inform acije o njegovom stanju po pra-
vilu se podešavaju već tokom projektovanja. O ne se m oraju sačuvati i rekonstruisati ka-
snije, kada se program pokrene. Taj zadatak obavlja serijalizovanje objekata.
Serijalizovanje objekta je prilično 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 podržale, uklju-
čujući i om otače 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 obično, dobija se referenca na obje-
kat klase Object, pa je neophodna konverzija naniže u ispravan tip.
Posebno pam etno rešenje prim enjeno u serijalizovanju objekata jeste to što se pored
čuvanja slike objekta prate i sve reference koje on sadrži, a čuvaju se i ti objekti, prate se
782 Misliti na Javi

njihove reference itd. Ovaj princip se ponekad naziva „m reža objekata“ povezana s jed-
nim objektom , i ona sadrži niz referenci na objekte i njihove članove. Kada biste m orali da
održavate sopstvenu šem u za serijalizovanje, bilo bi preteško napisati kod koji bi pratio
sve veze. Izgleda da serijalizovanje objekata u Javi om ogućuje da se to postigne bez po
muke, verovatno korišćenjem optim izovanog algoritm a koji krstari po mreži objekata. U
sledećem prim eru testira se m ehanizam serijalizovanja pravljenjem ,,crva“ od povezanih
objekata, pri čem u svaki pokazuje na sledeći segm ent crva i sadrži niz referenci na objekte
drugačije 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 slučajnim brojevim a. (Na taj način nećete posum njati da prevodilac zadržava kakve
metainform acije.) Svaki segm ent klase Crv označava se objektom tipa char koji se auto-
matski generiše tokom rekurzivnog pravljenja ulančane Iiste crva. Kada napravite objekat
klase Crv, saopštavate konstruktoru koliko treba da bude dugačak. Da bi napravio refe-
rencu na sledeci, on poziva konstruktora klase Crv s dužinom koja je m anja za jedan itd.
Poslednjoj referenci sledeci ostaje vrednost null koja ukazuje na to da je crv završen.
784 Misliti na Javi

Svrha svega bila je da se napravi nešto što je dovoljno složeno da se ne m ože lako seri-
jalizovati ručno. Postupak serijalizovanja, m eđutim , prilično je jednostavan. Kada se na-
pravi O bjectO utputStream od nekog drugog toka, serijalizuje ga m etoda w riteO bject().
O bratite pažnju i na poziv m etode w riteO b ject( ) za objekat tipa String. Pom oću istih
m etoda kao za D ataO utputStream m ožete da upisujete i sve proste tipove (oni koriste
isti interfejs).
Postoje dva zasebna odeljka koda koji izgledaju slično. Prvi upisuje i čita datoteku, a
drugi čita i upisuje u niz bajtova. Serijalizovanje om ogućuje čitanje ili upisivanje objekta
u bilo koji D atalnputStream ili D ataO utputStream , uključujući i mrežu, kao što je na-
vedeno u knjizi T hinking in Enterprisc Java.
Prim ećujete li da deserijalizovan objekat zaista sadrži 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 rekonstruiše na osnovu podataka iz ula-
znog toka.
Vežba 27: (1) Napravite klasu koja realizuje interfejs Serializable i sadrži referencu na ob-
jekat neke druge klase koja se m ože serijalizovati. Napravite instancu svoje klase, serijali-
zujte je tako da bude snim ljena na disk, zatim je rekonstruišite i proverite da li je postupak
ispravno sproveden.

Pronalaženje klase
Možda 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 računaru
kao datoteku ili preko mreže. Da li bi program na drugom računaru mogao da rekonstru-
iše objekat sam o na osnovu sadržaja datoteke?
Najbolji način da se odgovori na ovo pitanje jeste, kao i obično, da se proba. Sledeća
datoteka treba da se smesti u poddirektorijum ovoga poglavlja:

//: ui/Zeleni.java
// Klasa koja se može 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 obrađuje izuzetke, ovaj program ih iz m etode m a in ( ) prosleđuje 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 sledeći listing:

//: ui/dosijex/MaliZeleni.java
// Pokušaj da se serijalizovana datoteka rekonstruiše
// bez klase objekata uskladištenih 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 učitavanje objekta m isterija zahteva objekat klase Class za
klasu Zeleni; Javina virtuelna m ašina (JVM) neće moći da nađe datoteku Zeleni.class,
osim ako se ona nekim slučajem ne nađe u putanji klase, što nije slučaj u ovom prim eru,
pa će generisati izuzetak ClassNotFoundException. (Kao i obično, svaki dokaz o posto-
janju vanzem aljaca nestaje pre nego što se može proveriti njegova verodostojnost!) Javina
virtuelna m ašina m ora da pronađe odgovarajuću datoteku .class.

Upravljanje serijalizovanjem
Kao što vidite, podrazum evani m ehanizam serijalizovanja lako se koristi. M eđutim , šta
ako im ate specijalne potrebe? Možda posebno brinete o bezbednosti i zato ne želite da se-
rijalizujete delove objekta, ili m ožda nem a smisla da se neki podobjekat serijalizuje ako će
m orati ponovo da se pravi kada se objekat bude rekonstruisao.
Postupak serijalizovanja možete da kontrolišete realizovanjem interfejsa Externaliza-
ble um esto interfejsa Serializable. Interfejs Externalizable proširuje 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 omogućile izvršavanje
specijalnih operacija.
Sledeći prim er prikazuje jednostavne realizacije m etoda interfejsa Externalizable.
Klase Blipl i Blip2 su skoro iste, a malu razliku izm eđu njih prim etićete ako proučite kod:
786 Misliti na Javi

//: ui/Blipovi.java
// Jednostavno koriščenje 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 vraćamo:
ObjectlnputStream in = new ObjectInputStream(
new Fi1eInputStream("Blips.out"));
print(“Rekonstruisanje bl:“);
bl = (Blipl)in.readObject();
// UPS! Generiše 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 pokušaj 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 rekonstruiše, 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 rekonstruiše na osnovu sačuvanih podataka, bez pozivanja konstruktora. Uz
objekat interfejsa E xternalizable podrazum evani konstruktor se ponaša na uobičajen na-
čin (uključujući i inicijalizovanje tokom definisanja polja), a za tim se poziva m etoda re-
ad E xternal( ). O ovome m orate voditi računa, posebno o činjenici da se uvek pozivaju
podrazum evani konstruktori, kako biste postigli ispravno ponašanje objekata interfejsa
E xternalizable.
Evo prim era koji pokazuje šta m orate da uradite da biste sačuvali 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 vraćamo:
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,
pošto se m em orijska lokacija za nove objekte popunjava nulam a. Ako dva reda koda posle
rećenice „Ovo m orate da urađite" pretvorite u kom entare i pokrenete program , videćete
da, nakon rekonstruisanja objekta, s im a vrednost null, a i ima vrednost nula.
Ako izvodite nešto iz objekta tipa Externalizable, obično ć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 obezbeđili pravilno snim anje i
rekonstruisanje kom ponenata osnovne klase.
Poglavlje 18: Javin ulazno-izlazni sistem 789

Da bi sve radilo kako treba, znači da, pored upisivanja važnih podataka iz objekta to-
kom izvršavanja m etode w riteE xternal( ) (ne postoji podrazum evano ponašanje kojim
se upisuju bilo koji članovi objekta interfejsa Externalizable), m orate i da rekonstruišete
te podatke u m etodi readE xternal( ). To isprva m ože da zbuni zato što bi ponašanje pod-
razum evanog konstruktora objekta Externalizable m oglo stvoriti pogrešan utisak da se
neka vrsta pam ćenja i rekonstruisanja dešava autom atski. To se ne dešava.
Vežba 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 izvršite novi program .
Potom postavite znakove za kom entar ispred podrazum evanog konstruktora za klasu Pro-
veraBlipova. Izvršite program i objasnite zašto radi. O bratite pažnju na to da nakon pre-
vođenja m orate da izvršite program sa „java Blipovi" zato što se m etoda m a in ( ) i dalje
nalazi u klasi Blipovi.
Vežba 29: (2) U program u Blip3.java, pretvorite u kom entar po dva reda iza rečenica
„Ovo m orate da uradite:“ i pokrenite program . O bjasnite rezultat i zašto se on razlikuje
od slučaja kada su ta dva reda u program u.

Rezervisana reč transient


Ako upravljate serijalizovanjem, m ožda nećete želeti da Javin m ehanizam serijalizovanja
autom atski snim a i rekonstruiše neki podobjekat. To je često slučaj 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 pročita iz
datoteke ili da je presretne prilikom prenosa preko mreže.
Jedan način za sprečavanje serijalizovanja osetljivih delova objekta jeste da se klasa re-
alizuje kao Externalizable, kao što je upravo pokazano. Na taj način ništa se ne serijali-
zuje autom atski, to jest možete eksplicitno da serijalizujete sam o neophodne delove
u n u tar m etode w riteE x te rn a I().
M eđutim , ako radite sa objektom tipa Serializable, serijalizacija se obavlja autom at-
ski. Da biste to kontrolisali, serijalizovanje bilo kog elem enta m ožete da isključite rezer-
visanom rečju tran sien t, koja kaže: „Nemoj da se trudiš da ovo rekonstruišeš - ja ću se
postarati za to.“
Na prim er, posm atrajm o objekat P rijavljivanje koji čuva inform acije o određenom
prijavljivanju na sistem. Pretpostavim o da nakon provere ispravnosti prijavljivanja želite
da snim ite podatke, ali bez lozinke. To je najlakše uraditi tako što se im plem entira (reali-
zuje) interfejs Serializable a polje lozin ka označi kao tra n sie n t. Evo kako to izgleda:

//: ui/P ri ja vl jivanje.java


// Prikazuje korišćenje 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 + "korisničko 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); // Kašnjenje
// Sada ih vraćamo:
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 obična (a ne transient) i zato se autom atski


serijalizuju. M eđutim , polje lozinka je oznaćeno kao transient i zato se ne snim a na disk,
niti m ehanizam serijalizovanja pokušava da ga rekonstruiše. Kada se objekat rekonstrui-
še, polje lozinka im a vrednost null. O bratite pažnju na to da đok toString( ) sastavlja
String objekat preklopljenim operatorom +, referenca jednaka null autom atski se kon-
vertuje u znakovni niz null.
Možda ste uočili i to da je polje datum snim ljeno i rekonstruisano s điska, odnosno da
nije pravljeno ponovo.
Fošto objekti interfejsa Externalizable podrazum evano ne čuvaju nijedno polje, re-
zervisana reč transient koristi se isključivo 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 sviđa, isprobajte dru-
gačiji pristup. Možete da realizujete interfejs Serializable i da m u dodate (obratite pažnju
na to da sam rekao „dodate“, a ne ,,redefinišete“ 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 etođe, 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 tačke gledišta program iranja, ovo je prilično čudno. Prvo m ožete pom isliti da te me-
tode, pošto nisu deo osnovne klase ili interfejsa Serializable, treba da b u d u definisane u
sopstvenim interfejsima. O bratite pažnju na to da su one definisane kao privatne, što zna-
či da treba da ih pozivaju sam o drugi članovi iste klase. M eđutim , njih ne pozivaju drugi
članovi klase, već m etode writeObject( ) i readO bject( ) klasa ObjectO utputStream i
ObjectlnputStream . (Izuzetno se trudim da se ne upuštam u iscrpljujuću raspravu o
istim im enim a m etoda. Objašnjenje: sam o bih vas zbunio.) M ožda se pitate kako objekti
klasa ObjectO utputStream i O bjectlnputStream im aju pistup privatnim m etodam a
vaše klase. M ožemo sam o pretpostaviti da je to deo magičnog postupka serijalizovanja.6
U svakom slučaju, sve što se definiše 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.
Pošto m orate tačno da se pridržavate potpisa, učinak je isti kao da realizujete interfejs.
Izgleda kao da se posle poziva m etode O bjectO utputStream .w riteObject( ) objekat
interfejsa Serializable koji joj prosleđujete ispituje (bez sum nje, korišćenjem refleksije)
kako bi se ustanoviio cia li realizuje sopstvenu m etodu w riteO b ject( ). Ako je tako, pre-
skače se uobičajeni postupak serijalizacije i poziva nam enska m etoda w riteO bject( ). Isto
važi i za m etodu readO bject( ).
Postoji još jedna začkoljica. U nutar m etode w riteO bject( ) koju pišete možete da izve-
dete podrazum evanu akciju upisivanja objekta pozivom m etode defaultW riteO bject( ).
Slično, unutar m etode readO bject( ) možete da pozovete m etodu defaultR eadO bject( ).
Evo jednostavnog prim era koji pokazuje kako m ožete 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 vraćamo:
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 obično, a drugo je označeno kao transient
kako bi se dokazalo da m etoda defaultW riteO bject( ) snim a i rekonstruiše polje koje nije
transient, a da se transient polje snim a i rekonstruiše eksplicitno. Polja se iniđjalizuju 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 označeni 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 upiše sve delove koji nisu označeni sa transient. Da čoveka p odiđu žmarci.
Za čuvanje i rekonstruisanje polja označenih sa transient koristi se razumljiviji kod.
Ipak, razm islite o tom e šta se dešava. U funkciji m a in ( ) pravi se objekat KontroIaSerija-
lizovanja koji se p o tom serijalizuje u tok tipa ObjectOutputStream . (O bratite pažnju na
to da se u ovom slučaju um esto datoteke koristi m em orijski blok, kao i za ObjectO utput-
Stream.) Serijalizacija se dešava 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 traženjem m etode pom oću refleksije.) Ako pronađe tu m etodu, iskoristiće
je. Sličan pristup se koristi i u m etodi readO bject( ). M ožda je ovo bio jedini način za re-
šavanje problem a, ali je ipak čudan.

Zadavanje verzije
M ožda ćete hteti da prom enite verziju klase koja se m ože serijalizovati (na prim er, objekti
originalne klase m ogu biti čuvani u bazi podataka). To je podržano u favi, ali ćete to raditi
sam o u specijalnim slučajevima, a pri tom m orate bolje razum eti problem , što ovde neće-
mo razm atrati. HTM L dokum entacija razvojnog paketa za Javu koja se m ože preuzeti
s lokacije http:lljava.sun .co m , detaljno obrađuje ovu temu.
U ovoj dokum entaciji uočićete m noge kom entare koji počinju sa:
U pozorenje: Serijalizovani objekd ove klase neće biti ko m p a tib iln i s budućim verzijam a
Svvinga. T renutna podrška 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 previše jednostavan
da bi pouzdano radio u svim situacijam a, naročito sa zrnim a Jave. Radi se na ispravci pro-
jekta, o čem u upravo i govori ovo upozorenje.

Korišćenje trajnosti
Tehnika serijalizovanja je prilično privlačna za čuvanje stanja nekog program a da bi se on
kasnije m ogao lako vratiti nazad. M eđutim , pre toga, m ora se odgovoriti na neka pitanja.
Šta će se desiti ako se serijalizuju dva objekta, a svaki od njih sadrži referencu na treći
objekat? Kada rekonstruišete ta dva objekta iz serijalizovanog stanja, da li će se treći obje-
kat pojaviti sam o jednom ? Šta će se desiti ako se dva objekta serijalizuju u zasebne dato-
teke, a potom deserijalizuju u različitim 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("mačka 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 drugačiji 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 vraćamo:
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
, mačka 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
, mačka 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
, mačka 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
, mačka Cu ca [Z iv ot in ja @l cc a61 ], Kucica@lcca5c
]
* ///:-
Ovde je zanimljivo to da serijalizaciju objekata m ožete koristiti uz niz bajtova, kao na-
čin za tem eljno kopiranje svih objekata koji se m ogu serijalizovati. (Temeljno kopiranje
znači da se kopira čitava mreža objekata, a ne sam o osnovni objekat i njegove reference.)
Kopiranje objekata je detaljno obrađeno u m režnim dodacim a ove knjige.
Objekti klase Zivotinja sadrže 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. Kađa se ti tokovi deserija-
lizuju i ispišu, videčete rezultate prikazane za jedno izvršavanje (objekti će pri svakom po-
kretanju biti na različitim lokacijama u m em oriji).
Naravno, očekuje se da deserijalizovani objekti imaju drugačije adrese od originalnih.
M edutim , u objektim a zivotinjel i zivotinje2 pojavljuju se iste adrese, uključujući i refe-
rence na objekat klase Kucica koji oba objekta sadrže. 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 različitu m režu objekata.
Kad serijalizujete sve objekte u jedan tok, m oći ćete da rekonstruišete mrežu objekata
koju ste napisali, bez slučajnog dupliranja objekata. Naravno, možete 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 zabeležite stanje sistema, najbezbednije je da se to obavi kao „nedelji-
va“ operacija. Ako serijalizujete nešto, pa počnete da radite nešto drugo, zatim opet seri-
jalizujete itd., nećete moći bezbedno da zabeležite stanje sistema. Umesto toga, smestite
sve objekte koji odslikavaju stanje sistema u isti kontejner koji ćete snim iti u jednoj ope-
raciji. Na taj način moći ćete da ga rekonstruišete pozivom sam o jedne metode.
Sledeći prim er je sistem za projektovanje pom oću računara (CAD) koji ilustruje ovaj
princip. Pored toga, prim er obrađuje i statička polja: u dokum entaciji ćete videti da se ob-
jekat klase Class može serijalizovati, pa bi statička polja trebalo lako da se čuvaju tako što
bi se jednostavno serijalizovali objekti te klase. U svakom slučaju, to zasad izgleda kao pa-
m etan pristup.

//: ui/PamcenjeCADStanja.java
// Pamćenje 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 statičkih 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 ože serijalizovati. Svaki objekat klase Oblik sadrži podatke, a svaka klasa izvede-
na iz klase Oblik sadrži statičko polje koje određuje boju svih figura izvedenog tipa.
(Stavljanje statičkog polja u osnovnu klasu dalo bi samo jedno polje, pošto se statička
polja ne dupliraju u izvedenim klasama.) M etode osnovne klase se m ogu redefinisati da bi
se zadala boja različitih tipova oblika (za statičke m etode se ne prim enjuje dinam ičko po-
vezivanje, tj. radi se o običnim m etodam a.) M etoda slucajnoG enerisanje( ), kad god se
pozove, pravi različit objekat tipa Oblik tako što nasum ično bira tip oblika.
Klase Krug i Kvadrat jednostavna su proširenja 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 dložićem 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 ekonstrukđja objekata je prilično 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 uspešno snim aju i rekonstruišu, ali nešto nije
u redu sa rekonstruisanjem statičkih 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 statička polja uopšte nisu
serijalizovala! To je tačno: iako klasa Class može da se serijalizuje, ona ne radi ono što se
očekuje. Znači, ako želite da serijalizujete statičke podatke, to m orate da uradite sami.
U tu svrhu služe statičke m etode serijalizujStaticke( ) i deserijalizujStaticke( ) klase
Linija. V idite da se one eksplicitno pozivaju u postup ku snim anja i rekonstruisanja.
(O bratite pažnju na to da m ora biti održan 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 statičkih m etoda za serijalizovanje i deserijalizovanje.
Bezbednost je još jedna tem a o kojoj bi valjalo razmišljati, pošto se serijalizovanjem
čuvaju i privatni p o d ađ . Ako imate problem s bezbednošću, ta polja treba da označite kao
tra n sie n t. M edutim , tada m orate da smislite bezbedan način za čuvanje poverljivih in-
form acija tako da prilikom rekonstruisanja m ožete da vratite privatne prom enljive u pr-
vobitno stanje.
Vežba 30: (1) Popravite program CA D Stanje.java kako je opisano u tekstu.

XML
Važno ograničenje serijalizovanja objekata jeste njihova isključiva upućenost na Javu:
takve objekte m ogu da deserijalizuju samo Java program i. Konverzijom podataka u for-
m at XML dobilo bi se interoperabilnije rešenje koje bi mogle da koriste razne platform e
i jezici.
Zbog raširenosti XML-a postoji zbunjujuće velik broj opcija za program iranje koje ga
proizvode; m eđu njim a su i biblioteke javax.xm l.,<' koje se isporučuju uz razvojni paket
)ave. O dlučio 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 način 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 oću XOM-a proizvodi podatke o klasi O soba konvertovane u XML Elem ent,
i konstruktor koji prim a Elem ent i vadi odgovarajuće podatke o klasi O soba (obratite
pažnju 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 naći i u XOM dokum entaciji.
XOM sadrži 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 (), dobićete sve bačeno 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 ogućuje bezbedan
rad s tipovim a). Svaki Element u toj listi predstavlja objekat tipa Osoba, pa se predaje
drugom k onstrukto ru klase Osoba. Vodite računa o tom e da m orate unapred znati struk-
tu ru svoje XML datoteke, ali baš to je često slučaj u ovoj vrsti problem a. Ako ta struktura
ne odgovara onom e što očekujete, XOM će generisati izuzetak. Svakako da m ožete pisati
i složeniji kod koji će ispitati XML d o k um en t um esto da o njem u išta pretpostavlja, u
slučajevima kada im ate m anje konkretne inform acije o dolaznoj XML strukturi.
Da bi ovi prim eri mogli da se prevedu, m oraćete 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; više
inform acija potražite na adresi w w w .xo m .n u .
Vežba 31: (2) Program im a Osoba.java i Ljudi.java dodajte odgovarajuće podatke o adresi.
Vežba 32: (4) Koristeći M ap<String,Integer> i uslužnu klasu net.mindview. util.TextFi-
le, napišite program koji prebrojava reči u datoteci (kao drugi argum ent u konstruktoru
klase TextFile upotrebite "\\W +"). Rezultate sačuvajte u obliku XML datoteke.

Preferences
Interfejs za program iranje aplikacija (API) Preferences m nogo je bliži konceptu trajnosti
nego serijalizovanju objekata, zato što inform acije autom atski skladišti i vadi iz skladišta.
M eđutim , m ože se prim enjivati sam o na ođređene male skupove podataka - na proste
tipove i znakovne nizove (objekte tipa S tring), a svaki uskladišten znakovni niz ne sme
biti duži od 8 K (nije baš da je sićušno, ali nije za ozbiljne poslove). Kao što mu ime go-
vori, API Preferences služi za skladištenje i vadenje param etara koje je izabrao korisnik i
postavki konfiguracije program a.
Poželjni param etri i postavke sm eštaju se u skupove ključ - vrednost (kao m ape), us-
kladištene u hijerarhiji čvorova. lako se u hijerarhiji čvorova mogu napraviti složene
strukture, obično se pravi sam o jedan čvor nazvan po klasi o kojoj se inform acije skladište
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 papučice");
p r e f s .p u tl n t ( " D r u g a r a " , 4);
pr ef s.putBoolean("Ima li veštica?", 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
Obuća: Crvene papučice
Drugara: 4
Ima li veštica?: 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 poželjne param etre i postavke pojedinačnih korisnika, a ,,system“ za konfiguraciju
cele instalacije. Pošto je m etoda main( ) statična, za identifikaciju čvora upotrebljena je
klasa PrimerZaPreferences.cIass, ali u nutar nestatične m etode obično se koristi get-
C lass( ). Kao identifikator čvora ne m orate da koristite tekuću klasu, ali je to uobičajena
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 učitane različite vrste stavki, a zatim je pozvana m etoda
keys( ). N jen rezultat su ključevi (engl. keys) u obliku znakovnog niza (String[]), što poz-
navaoci istoim ene m etode iz biblioteke kolekcija ne bi očekivali. O bratite pažnju na drugi
argum ent m etode g e t( ). To je podrazum evana vrednost koja se vraća kao rezultat ukoliko
se vrednost za dati ključ ne pronade. Tokom iteracija kroz skup ključeva uvek znate da
neka stavka postoji, pa je korišćenje null kao podrazum evane vrednosti bezbedno. Uo-
bičajeno biste pribavljali neki imenovani ključ, kao u:

pr ef s. ge tI nt (" Dr ug ara ", 0));

U n orm aln om slučaju treba da zadate razum nu podrazum evanu vrednost. Zapravo,
ovo je jedan od tipičnih 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 način, U sageC ount će biti nula kada program pokrenete prvi put, ali će u kas-
nijim pokretanjim a biti različit od nule.
Kada pokrenete PrimerZaPreferences.java, videćete da se UsageCount zaista poveća-
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 odgovarajuće sistemske resurse, a oni se m enjaju u zavis-
nosti od operativnog sistema. U Windovvsu se za to upotrebljava registar (pošto je on io-
nako hijerarhija čvorova s parovim a ključ - vrednost). Poenta je sledeća: inform acije se
autom atski skladište, a mi ne m oram o da brinem o o tom e kako se to obavlja u bilo kojem
pojedinačnom sistemu.
804 Misliti na Javi

API Preferences um e više nego što je ovde prikazano. Dalje pojedinosti o tom e po-
tražite u dokum entaciji razvojnog paketa JDK, koja je prilično razumljiva.
Vežba 33: (2) Napišite program koji prikazuje tekuću vrednost direktorijum a nazvanog
„osnovni direktorijum “ i traži od vas njegovu novu vrednost. Za skladištenje te vrednosti
upotrebite API Preferences.

Sažetak
Javina biblioteka U/I tokova zadovoljava osnovne zahteve: om ogućuje čitanje sa konzole i
upisivanje u nju, u datoteke, u m em orijske blokove, čak i preko Interneta. Pom oću nasle-
đivanja možete da napravite nove vrste ulaznih i izlaznih objekata. Možete čak i da izmc-
nite način 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 ograničena autom atska konverzija tipa).
U dokum entaciji i projektu biblioteke U /I tokova, neka pitanja su ostala nedorečena.
Na prim er, bilo bi lepo kada biste mogli da generišete izuzetak pri pokušaju pisanja preko
postojeće datoteke tokom njenog otvaranja; neki program erski sistemi om ogućuju 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 oprečna osećanja; 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 zaokruže-
na: na prim er, ne bi trebalo da ja m oram da pišem uslužne klase kao što je TextFile (nova
(SE5) Javina klasa PrintVVriter predstavlja korak u pravom sm eru, ali to je tek deliinično
rešenje). Java SE5 je donela veliko poboljšanje: najzad je dodato form atiranje izlaza kakvo
oduvek podržavaju gotovo svi ostali jezici.
Kada shvatite dekoratorski projektni obrazac i počnete da upotrebljavate ovu bibliote-
ku tam o gde je potrebna njena prilagodljivost, uvidećete da je korisna i tada vam nekoliko
dodatnih redova koda više neće predstavljati problem .
R ešenja o d a b ra n ih vežbi 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 služi zapravljenje novog tipa od ograničenogskupa 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


čiščenje. M eđutim , pošto sada razum ete i neke kom plikovanije oblasti u Javi, m ožem o de-
taljnije da razm otrim o nabrojane tipove (engl. en u m era ted types ) u Javi SE5. Videćete da
s nabrojanim tipovim a m ože da se uradi i ponešto zanimljivo, ali ovo poglavlje bi trebalo
da vam pruži bolji uvid i u druge m ogućnosti jezika koje ste dosad upoznali, kao što su ge-
nerićki tipovi i refleksija. Naučićete i još nekoliko projektnih obrazaca.

Osnovne mogućnosti nabrojanih tipova


Kao što je bilo rečeno u poglavlju Inicijalizacija i čišćenje, kroz listu en u m konstanti
m ožete 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 možete (na prim er)
upotrebiti u petjji foreach.
Kada napravite nabrojan tip, prevodilac proizvodi njem u pridruženu klasu. O na
autom atski nasleduje klasu java.lang.E num , što joj daje određene m ogućnosti koje ću
prikazati u narednom prim eru:

//: nabrojani/KlasaEnum.java
// Mogućnosti 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, počev od nule. enum instance uvek možete 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, dobićete obuhvatajuću
enum klasu.
M etoda n am e( ) daje ime tačno onako kako je bilo deklarisano, a isto to dobijate i me-
todom to S trin g ( ). valueO f( ) je statični član klase Enum i proizvodi enum instancu koja
odgovara znakovnom nizu koji joj je prosleđen ili baca izuzetak ukoliko takav niz ne
može da pronađe.

Uvoz statičnih članova u nabrojani tip


Evo jedne varijacije program a Pljeskavica.java iz poglavlja Im cijalizacija i čišćenje:

//: 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 izričit i navesti
potpu na im ena svih enum instanci? To verovatno zavisi od složenosti koda. Prevodilac si-
gurno neće dopustiti da se upotrebi pogrešan 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 može prosuditi samo od slučaja do slučaja.
Vodite računa o tom e da ovu tehniku nije m oguće koristiti ako je enum definisan u
istoj datoteci ili podrazum evanom paketu. (Kao da je u kom paniji Sun bilo različitih
mišljenja o tom e treba li to dozvoliti.)

Dodavanje metoda nabrojanom tipu


Iz nabrojanog tipa ne m ožete izvoditi nove potklase, inače se enum može tretirati kao
obična klasa. To znači da mu možete dodavati m etode. enum čak može imati svoju me-
todu m a in ( ).
Videli ste da pođrazum evana inetoda to S trin g ( ) navodi sam o ime enum instance.
Ukoliko želite da date drugačiji opis određenog nabrojanog tipa, možete napraviti kon-
struktor koji će hvatati dodatne informacije i m etode koje će davati obim niji opis, kao u
sleđećem prim eru:

//: na br ojani/VesticalzOza.java
// Veštice iz Oza.
import static net.mind vi ew .u ti l.Print.*;

public enum VesticalzOza {


// Prvo morate definisati instance, pre metoda:
ZAPAD("Gospođica Galč, poznata i kao zla veštica sa zapada"),
SEVER("G1inda, dobra veštica severa"),
ISTOK("Zla veštica istoka, nosilac crvenih " +
"papučica, koju je smrvila Dorotina kuća"),
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: Gospođica Galč, poznata i kao zla veštica sa zapada
NORTH: Glinda, dobra veštica sa severa
EAST: Zla veštica sa istoka, nosilac crvenih papučica, koju je smrvila Doro-
tina kuča
SOUTH: Verovatno dobra, ali je nema
* ///:-

Ukoliko nam eravate da definišete m etode, sekvencu enum instanci m orate završiti
znakom tačka i zarez. Takođe, Java nas prisiljava da u nabrojanom tipu (enum ) najpre
definišemo instance. Izazvaćete grešku u vrem e prevođenja ako pokušate da ih definišete
nakon neke od m etoda ili polja.
K onstruktor i m etode im aju isti oblik kao u uobičajenim klasama, zato što ovo i jeste
obična klasa uz nekoliko ograničenja. Dakle, s nabrajanjim a m ožete raditi gotovo sve što
poželite (iako ćete se verovatno tru diti da ostanu prilično 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 može upotrebiti samo za pravljenje enuin
instanci koje ste deklarisali u n u tar definicije nabrojanog tipa; prevodilac neće dozvoliti da
ga upotrebite za pravljenje novih instanci nakon završetka te definicije.

Redefinisanje enum metoda


Sledi opis drugog načina proizvodnje drugačijih znakovnih nizova za nabrojane tipove. U
ovom slučaju, 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 uobičajenu 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 način na koji se oni mogu
u potrebiti u naredbam a switch. switch obično radi sam o s celobrojnim vrednostim a, ali
pošto nabrojani tipovi im aju utvrđen celobrojni poredak, a redni broj instance daje me-
toda o rd in a l( ) (izgleda da nešto slično radi prevodilac), nabrojane tipove m ožem o
upotrebljavati u naredbam a switch.
Iako im e enum instance obično m orate upotpuniti njenim tipom , to ne m orate da radite
u naredbi case. U sledećem prim eru enum je upotrebljen za pravljenje malc mašine 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 pažnju na to da u naredbi case
// ne mora te da pišete 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
uočio da za svaku instancu klase Signal im ate naredbe case. Neće se žaliti ni ako jednu od
naredaba case pretvorite u kom entar. Znači, sam i m orate paziti na to da definišete sve mo-
guće slučajeve. S druge strane, ukoliko iz naredbe case pozivate return, prevodilac će se ža-
liti ako nem ate default - čak i ako ste definisali sve moguće vrednosti tog nabrojanog tipa.
Vežba 1: (2) Izmenite Semafor.java pom oću uvoza statičnih č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 nasleđuju klasu Enum.
M eđutim , 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? Napisaćemo mali program koji će
to otkriti pom oću refleksije:

//: nabrojani/Refleksija.java
// Analiza nabrojanih tipova pomoću 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( ) statična 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 pronalaženje
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 označio sa final, pa od nje ne možete
izvoditi potklase. Tu je i statična ođređba za inicijalizaciju koja se može redefinisati
(pokazaču to kasnije).
812 Misliti na Javi

Zbog brisanja (opisanog u poglavlju Generički 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>.
Pošto je valu es( ) statična m etoda koju je u definiciju enum u m etn u o prevodilac, ako
neki od enum tipova svedete naviše na Enum, m etoda values( ) neće 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 ožete dobiti po-
m oću Class objekta:

//: nabrojani/SvodjenjeEnumaNavise.java
/ / Kada se nabrojan tip svede naviše,
// 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; // Svođenje naviše
// 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
* ///:-

Pošto m etoda getE num C onstants( ) pripada klasi Class, možete 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 vraća null, pa ćete dobiti izuzetak kada pokušate da upotrebite njen rezultat.
Poglavlje 19: Nabrojani tipovi 813

Realizuje, ne nasleđuje
Rckao sam da su svi nabrojani tipovi izvedeni iz klase java.lang.Enum . Pošto Java ne
podržava višestruko nasleđivanje, to znači da nabrojan tip ne m ožete napraviti preko na-
sleđivanja:

en um NijeMoguce extends Pet { . . . / / Ne radi

M eđutim , moguće je napraviti nabrojan tip koji realizuje jedan ili više interfejsa:

//: nabrojani/crtaci/EnumRealizacija.java
// Nabrojani tip može 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 neobičan, pošto m orate im ati instancu nabrojanog tipa da biste za nju
mogli pozvati m etodu. M edutim , L iklzC rtaca sada m ože da prihvati svaka m etoda koja
prim a G enerator, na prim er, m etoda p r in tN e x t().
Vežba 2: (2) Umesto da reali/.ujete interfejs, napravite n e x t( ) statičnom m etodom . Koje
su prednosti i m ane tog pristupa?
814 Misliti na Javi

Nasumičan izbor
U m nogim prim erim a u ovom poglavlju neophodan je nasum ičan izbor enum instanci,
kao što ste videli u m etodi LikIzCrtaca.next( ). To m ožem o uopštiti pom oću generičkih
tipova i rezultat sm estiti u zajedničku 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) ];
}
} ///= -

Prilično neobična sintaksa <T extends E n u m < T » opisuje T kao instancu nekog
nabrojanog tipa. O bjekat te klase činim o dostupnim tako što prosleđujem o Class<T>, i
zato se m ože napraviti niz enum instanci. Preklopljena m etoda ra n d o m ( ) treba da zna
sam o to da dobija T[], pošto ne m ora da obavlja Enum operacije; ona sam o nasum ično
bira neki elem ent niza. Povratni tip je tačno 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, videćete da njenom zaslugom u ovom pogiavlju


izbegavamo priličnu kolićinu dupliranja. D upliranje često dovođi do grešaka, pa je ukla-
njanje dupliranja koristan posao.
Poglavlje 19: Nabrojani tipovi 815

Upotreba interfejsa za organizovanje


To što je nem oguće naslediti nabrojan tip um e ponekad da zasmeta. Nasleđivanje nabro-
janog tipa potrebno je zbog povećavanja broja elem enata prvobitnog nabrojanog tipa i
zbog pravljenja potkategorija pom oću podtipova.
Kategorizaciju m ožete 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 različite
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--
Pošto nabrojan tip može imati samo podtipove napravljene realizacijom interfejsa,
svaki ugnežđeni en u in realizuje obuhvatajući interfejs H ran a. Sada m ožem o reći 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

Svođenje naviše na klasu H rana funkcioniše za svaki enum tip koji realizuje interfejs
H rana, pa su svi oni tipovi klase H rana.
M eđutim , za rad sa skupom tipova interfejs nije toliko upotrebljiv kao enum. Ukoliko
želite da im ate „nabrojan tip drugih nabrojanih tipova“, m ožete 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 odgovarajući objekat tipa Class kao
argum ent konstruktora od kojega m etodom g e tE n u m -C o n sta n ts() može da izvuče i
uskladišti 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 ožem o da napravim o nasum ično 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 slučaju, svrha pravljenja nabrojanog tipa nabrojanih tipova jeste iteracija kroz
sve tipove V rstajela. Kasnije, u prim eru A utom atZ aM aloprodaju.java, videćete drugačiji
pristup kategorizaciji određen drugačijim ograničenjim a.
Drugačiji, kom paktniji pristup problem u kategorizacije jeste ugneždivanje nabrojanih
tipova u n u ta r drugih nabrojanih tipova, što se radi ovako:

//: nabrojani/KategorijeVrednosnihPapira.java
// Sažetija 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 obuhvaćenih nabrojanih


tipova u jedan zajednički tip. Zatim se on i kategorizuju u zasebne nabrojane tipove
u nutar KategorijeVrednosnihPapira.
Ukoliko prim er H rana obradim o na ovaj način, 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 praktično isti kod, ali reorganizovan. U nekim slučajevima, tim e
se dobija jasnija struktura.
Vežba 3: (1) Dodajte nov tip Vrstajela u Vrstajela.java i pokažite da to radi u program u
Obrok.java.
Vežba4: (1) Ponovite p rethodnu vežbu za Obrok2.java.
Vežba 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-
juću kategoriju. Uputstvo: upotrebite argum ente promenljive dužine i ne zaboravite da oni
autom atski prave niz.
Vežba 6: (3) Daje li ugnežđivanje tipova Predjelo, Glavnojelo, Desert i Kafa unutar in-
terfejsa H rana ikakve prednosti nad slučajem kada bism o ih deklarisali kao samostalne
nabrojane tipove koji slučajno svi realizuju interfejs Hrana?

Skup EnumSet umesto indikatora


Skup (Set) jedna je vrsta kolekcije koja ne može sadržati više prim eraka istog tipa objekta.
N aravno, i za nabrojan tip je neophodno da svi njegovi članovi budu jedinstveni, pa se
utoliko ponaša kao skup, ali pošto m u ne m ožete 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 nekadašnje celobrojne„bit indikatore“ (engl.
b it Jlugs). Takvi indikatori se upotrebljavaju za čuvanje neke vrste inform acija tipa
uključeno/isključeno, ali m orate da radite s bitovim a um esto s konceptim a, pa je dobijeni
kod često nerazumljiv.
EnumSet radi brzo, pošto m ora da se takmiči s bit indikatorim a (njegove operacije su
obično m nogo brže nego one u skupu HashSet). Interno, EnumSet se (ako je moguće)
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 izražajniji način 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 ugrađen 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 može upotrebiti za praćenje 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]
* ///:-

Statičan uvoz pojednostavljuje korišćenje enu m konstanti. Imena m etoda su prilično


jasna sam a po sebi, a detalje m ožete pročitati u HTML dokum entaciji na Webu. Kada to
uradite, videćete nešto zanimljivo - m etoda o f ( ) je preklopljena i za argum ente pro-
menljive dužine i za pojedinačne m etode koje prim aju od dva do pet eksplicitnih argu-
m enata. To je pokazatelj brige za perform anse skupa Enum Set, pošto bi problem rešila
Poglavlje 19: Nabrojani tipovi 821

već i jed n a m etoda o f ( ) sa argum entim a prom enljive dužine, ali bi to bilo nešto 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, dobićete eksplicitne (nešto brže) m etode, ali ukoliko je pozo-
vete s jednim ili s više od pet argum enata, dobićete vararg verziju od o f ( ). O bratite pažnju
na sledeće: ukoliko je pozovete s jednim argum entom , prevodilac neće praviti vararg niz,
pa pozivanje te verzije s jednim argum entom neće povećati režijske troškove.
EnumSet se pravi povrh podupirućeg broja tipa long, long im a 64 bita, a svaka enum
instanca zahteva jedan bit za inform aciju uključeno/isključeno ili postojanje/neposto-
janje. To znači da EnumSet za jedan enum od najviše 64 elem enta troši sam o jedan broj
tipa long. Šta se dešava ako u nabrojanom tipu im ate više 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 očigledno nem a problem a s nabrojanim tipom koji im a više od 64 elementa,


pa m ožem o pretpostaviti da drugi long dodaje po potrebi.
Vežba 7: (3) Pronađite izvorni kod za Enum Set i objasnite kako radi.

Korišćenje mape EnumMap


E num M ap je specijalizovana m apa čiji ključevi m oraju poticati iz istog nabrojanog tipa.
Zbog ogranićenja kojima je podvrgnut enum , Enum M ap se interno može realizovati kao
niz. Zato je izuzetno brza, pa je slobodno možete koristiti za pretraživanja čiju osnovu
čine nabrojani tipovi.
Za ključeve u nabrojanom tipu možete 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 počinje interfejsom koji (najčešće) sadrži sam o jed n u m etodu; onda se
prave razne realizacije u kojim a se ta m etoda različito ponaša. 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'Požar 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 pronađe vrednost za određeni ključ:
em .g et (S AL A) .a ct io n();
} catch(Exception e) {
print(e);
}
}
} /* Ispis:
KUPATILO: Uzbuna u kupatilu!
KUHINJA: Požar u kuhinji!
java.lang.NullPointerExcepti on
* ///:-

Kao u skupu Enum Set, redosleđ elem enata u m api E num M ap određen je redosledom
njihovog definisanja u odgovarajućem 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
prevođenja.
Poglavlje 19: Nabrojani tipovi 823

Kao što ćete videti u nastavku poglavlja, m apam a EnumMap m ožemo obavljati višekrat-
no otkrivanje tipa u situacijama gde više nabrojanih tipova ulazi u m eđusobnu interakciju.

Metode koje se menjaju u zavisnosti od konstante


Javini nabrojani tipovi im aju veom a zanimljivu osobinu koja om ogućuje da svakoj enum
instanci definisanjem njenih m etoda date razlićito ponašanje. To ćete postići
definisanjem jedne ili više 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 ožete pronaći i pozvati preko njim a pridružene en u m instance. To se često


naziva kdd upravljan tabelom. (O bratite pažnju na sličnost s prethodno spom enutim
obrascem C om m and.)
U objektno orijentisanom program iranju, različita ponašanja se povezuju s različitim
klasama. Pošto svaka instanca nabrojanog tipa m ože im ati sopstveno ponašanje ostvare-
no m etodam a koje se menjaju u zavisnosti od konstante, stiče 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 ponašanje dobijam o
pozivom m etode g e t!n fo ().
824 Misliti na Javi

M eđutim , ta sličnost ne ide predaleko. en u m instance ne m ožete 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 može
} /* 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 generiše - svaki e n u m element je statična
finalna instanca tipa KaoKlase.
Takode, zbog svoje statičnosti, en u m instance u nutrašnjih nabrojanih tipova ne po-
našaju se kao obične unutrašnje klase; m i nem am o pristupa nestatičnim 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 može joj se pridružiti 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 točkova"); }


},
PRETPRANJE {
void d e j s t v o O { print("Omekšavanje nečistoće"); }
},
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("Nanošenje vrućeg 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("Sušenje 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 važan:
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
Nanošenje vrućeg voska
Ispiranje
Sušenje 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 trašnju klasu, ali sažetija.
U ovom p rim eru pokazana su i neka obeležja skupa E num Set. Pošto 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, pošto stanje bita sam o jed n o m možete da
prom enite u ,,uključeno“). Sem toga, nije važan redosled dodavanja en u m instanci
—redosled ispisivanja rezultata biva određen redosledom deklarisanja u nabrojanom tipu.
Da li je m oguće 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 ponašanje"); }
public static void main(String[] args) {
for(RedefinisanjeMetUZavOdK rmuzok : values()) {
printnb(rmuzok + " : ");
rm u z o k . f ();
}
}
} /* Ispis:
NAVRTKA: podrazumevano ponašanje
ZAVRTANJ: podrazumevano ponašanje
PODLOSKA: Redefinisana metoda
* ///:-

Iako nabrojani tipovi ne dozvoljavaju pisanje odredenih vrsta koda, po pravilu bi


trebalo da eksperim entišete s njim a kao da su klase.

Stvaranje lanca odgovornosti pomoću nabrojanih tipova


U projektnom obrascu C hain o f Responsibility (Lanac odgovornosti), definiše se više
različitih načina rešavanja odredenog problem a, a zatim se oni ulančavaju. Kada se pojavi
neki zahtev, on se prosleđuje duž lanca sve d ok jed n o od rešenja ne uzm ogne da ga obradi.
lednostavan Chain o f Responsibility lako je realizovati pom oću m etoda koje se
m enjaju u zavisnosti od konstante. Zamislite model pošte u kojoj se pokušava obrađi-
vanje svake pošiljke na najopštiji mogući način i tako sve dok se pošiljka ne proglasi za
neuručljivu. Svaki pokušaj m ožem o sm atrati jednim projektnim obrascem Strategy ; a
celokupnu listu za Chain o f ResponsibiIity.
Počećemo opisom pošiljke. Sva njena važna obeležja m ogu biti izražena nabrojanim
tipovim a. Pošto ćem o objekte tipa Posiljka generisati nasum ično, najlakše ćem o smanjiti
verovatnoću da će (na prim er) određena pošiljka dobiti DA za O pstaD ostava ukoliko
Poglavlje 19: Nabrojani tipovi 827

napravim o više ne-DA instanci, pa će vam definicije nabrojanih tipova spočetka izgledati
malo čudno.
U klasi Posiljka videćete m etodu nasum icnaPosiljka( ) koja pravi nasum ične prim er-
ke prob nih pošiljki. M etoda g en erato r( ) proizvodi objekat koji realizuje interfejs Itera-
ble i m etodom nasum icnaPosiIjka( ) proizvodi više pošiljki, po jednu za svaki poziv
m etode n e x t( ) putem iteratora. Takva konstrukcija om ogućuje jednostavno pravljenje
foreach petlje pozivom m etode P osiljka.generator( ):

//: nabrojani/Posta.java
// Modelovanje pošte.
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 verovatnoća nasumićnog 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 "Pošiljka " + id; }
public String details() {
return t o S t r i n g O +
", Opšta dostava: " + opstaDostava +
", Adresa se moze skenirati: " + MozeSeSkenirati +
", Čitljivost adrese: " + Citljivo +
", Adresa Adresa: 11 + adresa +
", Povratna adresa: " + povratnaAdresa;
}
// Generisanje probne Pošiljke:
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 opštu 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 + " : isporuči 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 + " : isporuči obično");
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 može uručiti");
}
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:
Pošiljka 0, Opšta 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
Pošiljka 0 : isporuči obično
*****
Pošiljka I, Opšta dostava: NE5, Adresa se moze skenirati: DA3, Adresa
čitljiva: NECITLJIVO, Adresa Adresa: 0K5, Povratna adresa: OKl
Pošiljka 1 : isporuči automatski
*****

Pošiljka 2, Opšta dostava: DA, Adresa se moze skenirati: DA3, Adresa


čitljiva: DAl, Adresa Adresa: OKl, Povratna adresa: 0K5
Pošiljka 2 : koristi opštu dostavu
*****
Pošiljka 3, Opšta dostava: NE4, Adresa se moze skenirati: DA3, Adresa
čitljiva: DAl, Adresa Adresa: NETACNA, Povratna adresa: 0K4
Pošiljka 3 : vrati pošiljaocu
* * * * *

Pošiljka 4, Opšta 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
Pošiljka 4 : vrati pošiljaocu
*****
Pošiljka 5, Opšta dostava: NE3, Adresa se moze skenirati: DAl, Adresa
čitljiva: NECITLJIVO, Adresa Adresa: 0K4, Povratna adresa: 0K2
83 0 Misliti na Javi

Pošiljka 5 : isporuči automatski


*****
Pošiljka 6, Opšta dostava: DA, Adresa se mo ze skenirati: DA4, Adresa
čitljiva: NECITLJIVO, Adresa Adresa: 0K4, Povratna adresa: 0K4
Pošiljka 6 : koristi opštu dostavu
* * * * *

Pošiljka 7, Opšta dostava: DA, Adresa se mo ze skenirati: DA3, Adresa


čitljiva: DA4, Adresa Adresa: 0K2, Povratna adresa: NEDOSTAJE
Pošiljka 7 : koristi opštu dostavu
* * * * *

Pošiljka 8, Opšta dostava: NE3, Adresa se mo ze skenirati: DAl, Adresa


čitljiva: DA3, Adresa Adresa: NETACNA, Povratna adresa: NEDOSTAJE
Pošiljka 8 se ne mo že uručiti
* * * * *

Pošiljka 9, Opšta dostava: NEl, Adresa se mo ze skenirati: NEMOZESESKENIRATI,


Adresa čitljiva: DA2, Adresa Adresa: OKl, Povratna adresa: 0K4
Pošiljka 9 : isporuči obično
* * * * *

* ///:-
Chain o f Responsibility je izražen u nabrojanom tipu P rZ aO b rad u P o ste, a redosled
en u m definicija određuje redosled isprobavanja strategija na svakoj pošiljci. Isprobava se
svaka strategija redom dok jedna ne uspe ili dok sve ne propadnu - u tom slučaiu pošiljka
se ne m ože uručiti.
Vežba 8: (6) Izm enite program Posta.java tako da pošiljke može da prosleđuje (s jedne na
drugu adresu).
Vežba 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 rešavaju
ulančavanjem u n a za d (engl. backw ard chaining). Uz Posta.java kao nadahnuče, proučite
takve jezike i napišite program koji om ogućava lako đođavanje novih „pravila" sistemu.

Mašine stanja s nabrojanim tipovima


N abrojani tipovi m ogu biti idealni za pravljenje m ašina stanja. Mašina stanja se može
nalaziti u jed no m od konačnog broja određenih stanja. Mašina obično prelazi iz jednog
stanja u drugo na osnovu ulaza, ali postoje i prelazna stanja; mašina izlazi iz njih čim oba-
vi njihove zadatke.
U svakom stanju dozvoljeni su određeni ulazi. Razni ulazi menjaju stanje mašine u
različita nova stanja. Pošto nabrojani tipovi ograničavaju skup m ogućih slučajeva, veoma
su podesni za nabrajanje različitih stanja i ulaza.
Svakom stanju obično je pridružen i neki izlaz.
D obar prim er m ašine stanja je m aloprodajni autom at. Prvo u nabrojanom tipu defi-
nišem o razne ulaze:

Projekti su predlozi koji se m ogu koristiti (recim o) za sem inarske radove. Vodič s rešenjim a ne sađrži
rešenja 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(() { // Onemogućiti
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() { // Onemogućiti
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 računa o tom e da je dvam a ulazima p ridružen određeni 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 generišu izuzetak ako je pozovete za njih. Iako je ovo neobično (definisati m e-
todu u interfejsu, a potom generisati izuzetak ukoliko je pozovete za neke realizacije), to
m oram o da radim o zbog ograničenja koja nameće rezervisana reč enum .
A u tom atZ aM aloprodaju će na ove ulazc reagovati tako što će ih najpre kategorizovati
pom oću 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 lakše
se održavao:

//: 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
* ///:-

Pošto se enu m instanca najčešće bira pom oću naredbe svvitch (obratite pažnju na do-
datni trud uložen u to da se naredba sw itch može lako upotrebljavati s nabrojanim tipovi-
ma), jedno od najčešćih pitanja u vezi sa upotrebom nabrojanih tipova jeste: ,,Po kojem
Poglav[je 19: Nabrojani tipovi 835

osnovu da biram?“ U navedenom prim eru je najlakše 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 isključenje mašine.
M eđutim , unutar tih kategorija imate različite novčane apoene koje kupac može ubaciti u
m ašinu i različite artikle koje može izabrati. Nabrojani tip Kategorija grupiše različite tipo-
ve ulaza tako da m etoda kategorizuj( ) može u nutar naredbe switch da proizvede odgova-
rajuću kategoriju. Ta m etoda delotvorno i bezbedno obavlja pretraživanje pom oću m ape
EnumMap.
Ako proučite klasu AutomatZaM aloprodaju, videćete da se sva stanja razlikuju i da
drugačije reaguju na ulaze. O bratite pažnju i na dva prelazna stanja; u m etodi r u n ( )
m ašina čeka na neki Ulaz i ne prestaje da m enja stanja dok ne izađe iz prelaznog stanja.
Autom atZaM aloprodaju m ožem o ispitati na dva načina, pom oću dva različita
objekta tipa Generator. Random lnputG enerator stalno proizvodi nove ulaze, sve sem
ulaza UGASI. Ukoliko dovoljno dugo izvršavate takav program , donekle ćete proveriti
ispravnost, tj. videćete da li je mašina sklona da odluta u loše 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 ograničenja ovog dizajna jeste to što polja klase Autom atZaM aloprodaju
kojima pristupaju instance nabrojanog tipa Stanje m oraju biti statična, što znači da
možete imati sam o jednu instancu klase AutomatZaM aloprodaju. To vas neće m nogo
zabrinuti ako pom islite na stvarnu (ugrađenu Java) realizaciju, pošto ćete verovatno imati
samo jednu aplikaciju po autom atu.
Vežba 10: (7) Izmenite (samo) klasu AutomatZaM aloprodaju koristeći EnumMap, tako
da program može imati više instanci klase AutomatZaM aloprodaju.
Vežba 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 ograničenja koja nabrojan tip nam eće na Ulaz
nepraktična (pošto enum definiše konačan 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 oću tekstualne datoteke inicijalizujte ArrayList tih objekata.
(U potrebite net.mindview.utiI.TextFile.)
Projekat: Projektujte m eđunarodni autom at za m aloprodaju (koristeći „intern ađ o n ali-
zaciju“ ), tako da se ista mašina može lako prilagoditi za različite zemlje.
836 Misliti na Javi

Vlšekratno otkrivanje tipa


Kada više tipova ulazi u m eđusobnu interakciju, program se lako zapetlja. Na prim er,
razm otrite sistem koji raščlanjuje i izračunava m atem atičke izraze. Hteli biste da kažete
Broj.plus(Broj), Broj.pomnozi(Broj) itd., gde je Broj osnovna klasa neke porodice nu-
m eričkih objekata. Ali kada kažete a.plus(b), a ne znate tačan tip ni od a ni od b, kako če
oni ući u interakciju?
O dgovor počinje nečim o čem u verovatno nikada niste razmišljali: Java obavlja samo
jednokratno otkrivanje tipa (engl. single dispatching). D rugim rečima, ukoliko obavljate
operaciju s više objekata nepoznatih tipova, Java može da pozove m ehanizam dina-
mičkog povezivanja sam o za jedan od njih. Tim e se ne bi rešio p rethodno opisani pro-
blem, pa neke tipove m oram o sam i (ručno) da otkrijem o i tako napravim o sopstveno
dinam ičko povezivanje.
Rešenje se naziva višekratno otkrivanje tipa (engl. m ultiple dispatching). (U ovom slu-
čaju biće sam o dva otkrivanja - to je dvokratno otkrivanje tipa.) Polimorfizam je moguć
sam o preko poziva m etoda, pa ako hoćete 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 višekratnom otkrivanju tipa, m orate im ati po jedan virtuelni poziv za svaki od nepo-
znatih tipova - ukoliko im ate posla s dve različite 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 više virtueinih poziva m etoda i tim e opslužuje
više tipova. To ćete postići pom oću više 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 oguća 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 višekratnog 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 više 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 višekratno otkrivani. RoSham B ol.m atch( )
prim a dva objekta tipa Stavka i postupak dvokratnog otkrivanja tipa otpočinje 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. Prosleđivanjem sebe (this) kao argu-
m enta m etode iz rc ( ) proizvodi poziv preklopljene funkcije iz r c ( ), pa ostaje sačuvana
inform acija o tipu dobijena u prvom otkrivanju tipa. Kada se dovrši drugo otkrivanje
tipa, znam o tačan tip oba objekta tipa Stavka.
Program iranje višekratnog otkrivanja tipa zahteva veliku cerem oniju, ali ne gubite iz
vida dobijenu sintaksičku eleganciju sam og poziva - um esto da pišete nezgrapan kod za
utvrđivanje tipa jednog ili više objekata tokom nekog poziva, jednostavno kažete: „Vas
dvoje! Ne zanim a me koji su vaši tipovi, sam o uđite propisno u m eđusobnu interakciju!“
M eđutim , neka vam ta vrsta elegancije postane važna pre nego što krenete na višekratno
otkrivanje tipa.

Otkrivanje tipa pomoću nabrojanih tipova


Prosto prevođenje program a RoShamBol.java u rešenje čiju osnovu čine nabrojani tipo-
vi nije jednostavno, jerenum instance nisu tipovi, pa preklopljene m etode i z r c ( ) neće ra-
diti - enum instance ne m ožete upotrebljavati kao tipove u argum entu. M eđutim,
realizacija višekratnog otkrivanja tipa p om oću nabrojanih tipova može se obaviti na više
različitih načina.
U jednom načinu upotrebljava se k o nstru k to r za inicijalizaciju svake en u m instance
celim ,,ređom“ ishoda; zajedno uzev, tim e se dobija svojevrsna tabela za pronalaženje:

//: nabrojani/RoShamBo2.java
// Izbor konstante jednog nabrojanog tipa
// korišćenjem 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 vraćanje rezul-
tujućeg objekta tipa Ishod. M eđutim , 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 lakše
i pratiti. Vodite raćuna 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 ograničava broj izbora u naredbi switch.
840 Misliti na Javi

Kod koji upravlja nabrojanim tipom tako je raščlanjen da ga m ožem o koristiti u drugim
prim erim a. Prvo, interfejs Takmicar definiše tip koji se takmiči 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 definišemo dve statične m etode (statične 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 slučaju 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 prosleđuje m etodi
m a tc h ():

//: nabrojani/RoShamBo.java
// Zajedničke 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 vređnost koja obuhvata param etar tipa T, pa izgleda
kao da bism o mogli upotrebiti džokerske argum ente u n u tar tipa Class<T> um esto što
koristim o opis vodećih param etara. M eđutim , džokerski argum enti se ne m ogu protezati
na više od jednog osnovnog tipa, pa m oram o da koristim o gornji izraz.

Korišćenje metoda koje se menjaju u zavisnosti od konstante


nabrojanog tipa
Pošto m etode koje se menjaju u zavisnosti od konstante nabrojanog tipa om ogućuju pra-
vljenje različitih realizacija m etoda za svaku enum instancu, m ožda vam izgleda da su
Poglavlje 19: Nabrojani tipovi 841

one savršeno rešenje za višekratno otkrivanje tipa. Ali iako n a taj način m ogu dobiti
drugačija ponašanja, enum instance nisu tipovi, pa ih ne m ožete koristiti kao argum ente
tipova u potpisim a metoda. U ovom prim eru upotrebljavate naredbu svvitch, i to je naj-
više što se m ože 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, rešenje u program u RoSham Bo2.java kao da zahteva
m anje koda za dodavanje novog tipa, te izgleda jednostavnije.
M eđutim , RoSham Bo3.java m ožem o pojednostaviti i sažeti:

/ / : 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 poređenja i stoga radi slično naredbi switch. Program je manji, ali
i m anje jasan. U velikom sistem u ta nejasnost može imati strašne posledice.

Otkrivanje tipa pomoću mapa EnumMap


,,Pravo“ dvokratno otkrivanje tipa m ožem o obaviti pom oču klase EnumMap koja veoma
efikasno radi s nabrojanim tipovim a. Pošto je naš cilj pravljenje izbora na osnovu dva ne-
poznata tipa, za dvokratno otkrivanje tipa m ožem o upotrebiti EnumMap čiji su članovi
drugi objekti tipa EnumMap:

//: na brojani/RoShamBo5.java
// Višekratno otkrivanje tipa pomoću 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 statičnoj odredbi; struk tu ra poziva m etode in ic R e d a ()


nalik je na tabelu. O bratite pažnju na m etodu ta k m ic e n je (), gde se oba otkrivanja tipa
obavljaju u istoj naredbi.

Korišćenje 2-D niza


Rešenje m ožem o još pojednostaviti kada uočim 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 rešenje (a m ožda i najbrže, m ada ne
treba zaboraviti da Enum M ap upotrebljava interni niz) daje dvodim enzionalni niz koji
takm ičare preslikava u ishode:

//: nabrojani/RoShamBo6.java
// Nabrojani tipovi i "tabele" umesto
// višekratnog 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 tačno onaj poredak kao pozivi m etode in ic R e d a () u p rethodnom prim eru.
Ovaj kratak kod deluje m nogo privlačnije 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 veći, lakše je pogrešiti u veličini, i ako ispitivanje ne obuhvati sve m o-
gućnosti, nešto bi moglo da procuri.
Sva ova rešenja predstavljaju različite vrste tabela, ali ih vredi istražiti da bismo
pronašli najprikladniju. Vodite računa o sledećem: iako je poslednje rešenje najkom pakt-
nije, ono je i prilično neprilagodljivo, zato što m ože da proizvede sam o konstantan izlaz
za date konstantne ulaze. M eđutim , ništa vas ne sprečava da pom oću takve tabele napra-
vite funkcijski objekat. Pri određenim vrstam a problem a, koncept „koda upravijanog ta-
belom “ um c veoma dobro da posluži.

Sažetak
Iako nabrojani tipovi sami po sebi nisu previše složeni, ovo poglavlje sam pom erio u dru-
gi deo knjige zbog onoga što se m ože uraditi kom binacijom nabrojanih tipova i polim or-
fizma, generičkih tipova i refleksije.
M ada znatno sofisticiraniji nego u jezicim a C i C + + , nabrojani tipovi i dalje pred-
stavljaju ,,malu“ m ogućnost, nešto bez čega je jezik opstajao (pom alo nezgrapno) mnogo
godina. Ovo poglavlje pokazuje kako m ože biti važan doprinos ,,male“ m ogućnosti - kat-
kada nam daje baš onu polugu kojom problem m ožem o rešiti elegantno i jasno, a u celoj
knjizi sam isticao koliko je elegancija važna. Jasnoća m ože biti činilac koji određuje da li
je rešenje uspešno ili nije dobro zato što ostali ne m ogu da ga razum eju.
Što se tiče jasnoće, Java 1.0 je proizvela zbrku zbog korišćenja term ina ,,enum eration“
um esto uobičajenog i prihvaćenog term ina „iterator" za objekat koji bira svaki element
neke sekvence. U nekim jezicim a čak se i nabrojani tipovi nazivaju ,,enum erators“! Ta
greška je ispravljena u Javi, ali interfejs E n u m e ra tio n nisu smeli sam o tako da izbace, pa
se još viđa u starim (a ponekad i u novim !) program im a, biblioteci i dokum entaciji.
R ešenja o d a b r a n ih vežbi 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 način dodavanja inform acija kodu, što olakšava
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 nešto slično postoji i u drugim jezicima, C#-u, na primer.
A notacije su jedna od osnovnih jezičkih prom ena u Javi SE5. U njih se stavljaju infor-
macije koje ne m ogu biti izražene Javom, a potrebne su za po tp u n o opisivanje program a.
Dakle, anotacije om ogućuju skladištenje 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 olakšavaju teret pisanja ,,šablonskog“ koda.
Pom oću anotacija, te m etapodatke m ožete zadržati u Javinom izvornom kodu, i uz to
im ate čistiji kod, m ogućnost provere tipova u vrem e prevođenja i pom oć odgovarajućeg
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 prilično jednostavna i svođi se uglavnom na dodavanje simbola @
u jezik. Java SE5 sadrži tri ugrađene anotacije opšte nam ene, definisane u paketu java.lang:
• @ O verride pokazuje da definicija neke m etode redefiniše m etodu osnovne klase.
Prevodilac će generisati grešku ukoliko slučajno pogrešno napišete ime te m etode ili
joj date netačan potpis.2
• @ D eprecated, zbog koje prevodilac generiše upozorenje ukoliko taj elem ent bude
upotrebljen.
• @ SuppressW arnings, za isključivanje neprikladnih upozorenja prevodioca. U ra-
nijim izdanjim a Jave SE5 dozvoljavala se ova anotacija, ali nije bila podržavana (ne-
go ignorisana).
Za pravljenje novih anotacija služe četiri druga tipa anotacija koje ću opisati u ovom
poglavlju.
Kad god pišete opisne klase ili interfejse s m nogo ponavljanja, taj posao možete da au-
tom atizujete i pojednostavite pom oću anotacija. Prim era radi, dobar deo dodatnog posla
u Enterprise JavaBeans, EJBs, više se ne m ora raditi otkad u EJB3.0 postoje anotacije.
A notacije m ogu da zam ene postojeće sisteme kao što je X D ođet —alatka nezavisnog
proizvođača 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 iše 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 jezičke konstrukcije, im aju stru k tu ru , a njihovi tipovi se proveravaju u vrem e pre-
vođenja. Kod je elegantniji i lakše se održava ukoliko su sve inform acije zadržane u
izvornom kodu, a ne u kom entarim a. K orišćenjem i nasleđivanjem API-ja i alatki za
anotacije, Oi pom oću spoljnih biblioteka za rad s bajtkodom koje ćete upoznati u ovom
poglavlju, možete obaviti dalekosežnu 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 ništa, ali će prevodilac proveriti im ate li deftniciju anotacije @Test u putanji au-
tom atskog prevođenja i pakovanja (builda). Kao što ćete videti u nastavku poglavlja,
pom oću refleksije m ožete napraviti alatku koja će tu m etodu izvršavati.

//: anotacije/MozeSeTestirati.java
package anotacije;
import net.mindview.atunit.*;

public class MozeSeTestirati {


public void izvrsi() {
System.out.pri ntln("Izvršavam..");
}
@Test void probnoIzvrsenje() { izvrsi(); }
} ///•-

A notirane m etode se ne razlikuju od ostalih m etoda. A notaciju @Test iz prethodnog


prim era, možete koristiti u kom binaciji sa svim m odifikatorim a kao što su public ili sta-
tic ili void. Sintaksički, anotacije se upotrebljavaju veom a slično m odifikatorim a.

Definisanje anotacije
Sledi definicija gornje anotacije. Videćete da definicije anotacija mogo liče 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 liči na prazan interfejs. Definicija anotacije
zahteva i m etaanotacijc @Target i @ Retention. @Target definiše gde se anotacija može
prim eniti (recimo, ispred m etode ili polja). @ Retention definiše 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 najčešće sadrže 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 liče na m etode interfejsa, sem što im možete definisati podrazum evane vrednosti.
A notaciju bez elem enata, kao što je @Test, nazivam o m arker anotacija.
Sledi jednostavna anotacija koja prati slučajeve upotrebe u projektu. Program eri ano-
tiraju svaku m etodu ili skup m etoda koje zadovoljavaju zahteve određenog slučaja
upotrebe. Kad prebroji realizovane slučajeve upotrebe, rukovodilac projekta može steći
uvid u napredovanje projekta, a program eri koji održavaju projekat lako pronalaze sluča-
jeve upotrebe kada treba da ažuriraju poslovna pravila u sistem u ili da otkriju i otklone
greške 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 pažnju na to da id i opis liče na deklaracije m etoda. Pošto prevodilac prove-


rava tip od id, to je pouzdan način povezivanja baze podataka za praćenje s dokum entom
slučaja upotrebe i sa izvornim kodom . Element opis ima podrazum evanu vrednost koju
će procesor anotacija upotrebiti ukoliko neka druga vrednost ne bude zađata prilikom
anotiranja metode.
U sledećoj klasi tri m etode su anotirane kao slučajevi 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 može 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 izražene su kao parovi im e-vrednost u zagradam a iza
deklaracije anotacije @SlucajUpotrebe. Anotaciji m etode sifrirajLozinku( ) ovde nije
bila prosleđena 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 vašeg 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 može primeniti. Mogući E!ementType argumentijesu:


CONSTRUCTOR: deklaracija konstruktora
FIELD : deklaracija p o p (ukjjučujući i enum konstante)
LO CA L VARIABLE: deklaracija lokalnih promenljivih
M ETHOD: deklaracija metoda
PACKAGE: deklaracija paketa
PARAMETER: deklaracija parametara
TYPE: deklaracija klasa, interfejsa (uključujući i tip anotacije) ili nabrojanih tipova
©Retention Koliko dugo se čuvaju informacije anotacije. Mogući RetentionPolicy argumenti
jesu:
SO URCE: anotaciju prevodilac izbacuje.
CLASS. prevodilac ostavlja anotaciju u datoteci klase, ali je VM može
izbaciti.
RUNTIME VM ostavlja anotaciju u vreme izvršavanja. pa ona može biti pročitana
pomoću 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
obrađivati.

Pisanje procesora anotacija


Bez alatki koje će ih čitati, anotacije teško da su korisnije od kom entara. Važan deo
postupka korišćenja anotacija jeste pisanje i korišćenje procesora anotacija. Kao pom oć za
pravljenje tih alatki, u Javi SE5 koriste se proširenja API-ja za refleksiju. Postoji i spoljna
alatka ap t za analizu (raščlanjivanje) Java izvornog koda sa anotacijam a.
Sledi veom a jednostavan procesor anotacija koji čita anotiranu klasu U sluzneM etode-
ZaLozinke i refleksijom traži oznake @ SlucajU potrebe. Za datu listu id vrednosti,
ispisaće pronađene slučajeve 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 :
Pronađen 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
Pronađen s l u č a j u p o tr e b e :4 9 Nova l o z i n k a ne može 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 potiče iz interfejsa AnnotatedElem ent (koji realizuju klase
kao što su Class, M ethod i Field). Ta m etoda vrača objekat anotacije specificiranog tipa,
u ovom slučaju tipa SlucajUpotrebe. Ukoliko nem a anotacija tog odredenog tipa za ano-
tiranu m etodu, vrača 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 sadrži 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 grešku ako pokušate da upotrebite neki đrugi tip. Vodite računa
0 tom e da ne smete koristiti om otačke klase, ali zbog autom atskog pakovanja to zapravo
1nije ograničenje. Dozvoljeni su i elem enti koji su sami anotacije. Kao što ćete videti malo
kasnije, ugneždene anotacije m ogu veom a dobro da posluže.

Ograničenja 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 znači da elementi m oraju imati
podrazum evane vrednosti ili vrednosti zadate u klasi koja upotrebljava anotaciju.
Postoji još jedno ograničenje: elem enti neprostih tipova ne smeju poprim iti vrednost
null. To važi i za deklaracije u izvornom kodu i za definicije podrazum evanih vrednosti u
interfejsu anotacije. Ovo otežava pisanje procesora koji deluje na osnovu postojanja ili
nepostojanja određenog elem enta, pošto je svaki elem ent efektivno prisutan u svakoj de-
klaraciji anotacije. Prevazići ćete ovu poteškoću tako što ćete tražiti specifične 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 tipičan za definicije anotacija.

Generisanje spoljnih datoteka


Anotacije su naročito 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 ođnosu 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 obično
prouzrokuje problem e sa sinhronizacijom . Zbog toga program eri koji rade na projektu,
pored toga kako se piše Java program , m oraju znati i kako da izmene deskriptor.
Pretpostavim o da pišete osnovne funkcije za preslikavanje objekata na relacione baze
podataka; te funkcije autom atizuju pravljenje tabele baze podataka za skiadištenje zrna
Jave. Mogli biste upotrebiti XML datoteku deskriptora koja specificira ime klase, njene
članove i inform acije o njenom preslikavanju u bazi podataka. M eđutim , pom oću
Poglavlje 20: Anotacije 851

anotacija, sve te inform acije zadržali biste u izvornoj datoteci zrna Jave. Za to bi vam bile
potrebne anotacije koje definišu im e tabele baze podataka pridružene 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) / / Važi 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 ograničenje koje prevo-
diocu kazuje da se ta anotacija može prim eniti sam o na taj određeni tip. Za nabrojani tip
ElementType m ožete specificirati jednu vrednost ili listu proizvoljnih kom binacija vred-
nosti razdvojenih zarezima. Ako anotaciju želite da prim enite na svaki ElementType,
m ožete p o tp u n o da izostavite anotaciju @Target, mada to nije uobičajeno.
O bratite pažnju na to da @TabelaBP im a elem ent im e ( ), tako da anotacija može 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 ogućuje procesoru da izdvoji metapodatke o tabeli baze
podataka. Oni predstavljaju tek mali podskup ograničenja koje baze podataka po pravilu
nam eću, ali dovoljan da steknete opštu sliku. Elementima prim arniK Ijuc( ), dozvoliN ull()
i jedinstveno( ) date su smislene podrazum evane vrednosti, tako da korisnik anotacije u
većini slučajeva ne m ora m nogo da kuca.
Ostala dva interfejsa (@interface) definišu SQL tipove. I opet, da bi ova struktura bila
upotrebljivija, treba da definišete 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 sadrži inform acije o ograničenjim a baze podataka za tip kolone. O bratite
pažnju 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 ugneždenoj anotaciji @Ogranicenja sa
jedinstvenošću postavljenom na podrazum evanu vrednost true, njen elem ent možete
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 ugnežđenih 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 oću @SQLStringova i imaju
vrednosti 30 odnosno 50. Te anotacije su zanimljive iz dva razloga: prvo, koriste
podrazum evanu vrednost ugnežđene anotacije @Ogranicenja, i drugo, koriste kraći ob-
lik pisanja. Ako anotacija im a sam o jedan elem ent i vi m u date im e value, ne m orate da
pišete parove im e-vrednost; dovoljno je da specificirate sam o vrednost (engl. value) u za-
gradam a. To važi za sve dozvoljene tipove elem enata. N aravno, tada elem entu m orate dati
im e „vrednost", ali u gornjem slućaju ipak dobijate sem antički 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
složena. 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 ugnežđenoj anotaciji @Ogranicenja. Tu stvari postaju zapetljane. Za tu
ugnežđenu anotaciju m orate upotrebiti opširn u sintaksu im e-vrednost, u kojoj ponovo
specificirate ime elem enta i ime interfejsa (@interface). Ali pošto posebno imenovani
elem ent value više nije jedini elem ent čija se vrednost zađaje, ne m ožem o upotrebiti kraći
oblik sintakse. Kao što vidite, rezultat ne izgleda baš lepo.

Drugačija rešenja
U ovom slučaju im a i drugih načina da se naprave anotacije. Na primer, m ožem o napraviti
samo jednu klasu anotacije @KolotiaTabele u čijem enum elem entu definišemo 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 ogućnost da kvalifikujemo tipove pom oću do-
datnih elemenata kao što su velicina ili preciznost , pa je šteta verovatno veća od koristi.
Stvarni SQL tip m ožem o opisati String elem entom , npr. VARCHAR(30) ili INTEGER.
Time ne bism o izgubili m ogućnost da kvalifikujem o tipove, ali bi preslikavanje }ava tipa
u SQL, tip bilo zaključano 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 saopštiti 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

Treće rešenje bilo bi korišćenje dva tipa anotacija, @ Ogranicenja i relevantnog SQL
tipa (recimo, @SQLInteger) za anotiranje željenog polja. To jeste malo zapetljano, ali
prevodilac dozvoljava proizvoljan broj različitih anotacija za svako odredište (engl. tar-
get). Kada koristite više anotacija, istu anotaciju ne sm ete u potrebiti dvaput.

Anotacije ne podržavaju nasleđivanje


Rezervisanu reč extends ne smete koristiti uz @interface. To je šteta, pošto bi elegantno
rešenje bilo da definišemo anotaciju @KolonaTabeIe (kao što sam predložio gore) uz
ugnežđenu 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 nagoveštaja da će u budućim izdanjim a anotacije podržati na-
sleđivanje, pa izgleda kao da su gornji prim eri ono najviše što se m ože uraditi u datim
okolnostima.

Realizacija procesora
Sledi prim er procesora anotacija koji učitava datoteku klase, traži u njoj anotacije baze
podataka i generiše 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
učitava m etoda forN am e( ) i naredbom getAnnotation(TabelaBP.class) proverava im a li
anotaciju @TabelaBP. Ukoliko ima, ime te tabele biva pronađeno i uskladišteno. Zatim se
sva polja te klase učitavaju i proveravaju m etodom getD eclaredA nnotations( ). O na
vraća 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 sluča-
jeva pravi se relevantni String odlom ak sa im enom kolone tabele. Pošto se interfejsi ano-
tacija ne m ogu nasleđivati, korišćenje m etode getD ecIaredA nnotations( ) jedini je način
da se postigne približno polim orfno ponašanje.
Ugnežđenu anotaciju @Ogranicenja prosleđujem o m etodi dajO granicenja( ) koja
gradi String što sadrži ta SQL ograničenja.
Vredi spom enuti da je goreopisana tehnika pom alo naivan način definisanja preslika-
vanja objekata na relacione baze podataka. Ako prom enite im e tabele, m oraćete iznova da
prevodite Java kod, pošto anotacija tipa @TabelaBP prim a ime tabele kao param etar. To
nije naročito poželjno ponašanje. Već je m nogo dostupnih struktura (engl. fram ew ork) za
preslikavanje objekata na relacione baze podataka, a u sve više njih koriste se anotacije.
Vežba 1: (2) Realizujte više SQL tipova u prim eru s bazom podataka.
Projekat:’ Izm enite prim er s bazom podataka tako da se pom oću 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-
govarajuće 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. Pošto pređstavlja prvi pokušaj, malo je prim itivna, ali
ipak m ože da posluži.
Kao i javac, apt obrađuje izvorne datoteke, a ne prevedene Java datoteke. Kada završi
obradu izvornih datoteka, apt ih podrazum evano prevodi. To je korisno ako autom atski
pravite nove izvorne datoteke u sldopu postupka prevođenja i pakovanja (build). U stvari,
apt u istom prolazu traži anotacije u novonapravljenim izvornim datotekam a i prevodi ih.
Kada procesor anotacija napravi novu izvornu datoteku, anotacije u njoj traži u sledećoj
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 napišete potreban je sopstveni procesor, ali alatka apt m ože da
grupiše više procesora. Njoj m ožete zadati više kJasa za obradu, što je m nogo lakše nego
da sam i iterirate kroz klase File. Možete dodati i osluškivaće koji će vas obavestiti kada se
svaka ru n d a obrade anotacija završi.
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, očigledno je da
se m ože 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 sadrži i interfeise
com.sun.mirror.*.
apt radi tako što za svaku anotaciju koju pronađe upotrebi AnnotationProcessorFac-
tory za pravljenje odgovarajuće vrste procesora anotacija. Kada pokrećete apt, zadajte
proizvođačku klasu ili putanju klasa gde apt može naći proizvođačke ldase koje su m u
potrebne. Ako to ne uradite, apt će se otisnuti na tajnoviti postupak otkrivanja, čije poje-
dinosti m ožete naći u odeljku D evelopingan A n n o ta tio n Processor (Razvojprocesora an o -
tacija) Sunove dokum entacije.
Javinu sposobnost refleksije ne možete koristiti u procesoru anotacija za rad sa
alatkom apt, pošto radite sa izvornim kodom a ne s prevedenim klasama.4 API m irro r’
rešava taj problem tako što om ogućuje da vidite metode, polja i tipove u neprevedenom
izvornom kodu.
Sledeću anotaciju m ožete 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 pomoću 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 vređnost SOURCE zato što ovu anotaciju nem a
smisla čuvati u đatoteci klase nakon što iz nje izdvojimo interfejs. Sledeća klasa ima javnu
m etodu koja m ože 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 pomoću 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 više p u ta poziva privatnu m etodu a d d ( ) da bi obavila m noženje. 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 pomoću 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 služi za slanje poruka korisniku, npr. grešaka koje su nastale tokom obra-
de i njihovih m esta u izvornom kodu. Filer je neka vrsta PrintW ritera za pravljenje novih
datoteka. Umesto običnog PrintW ritera najčešće se koristi objekat tipa Filer zato što
om ogućuje alatki apt da prati nove datoteke koje napravite i, ako treba, pronalazi u njim a
anotacije i prevodi ih.
Videćete i da m etoda createSourceFile( ) otvara običan izlazni tok s tačnim im enom
odgovarajuće Java klase ili interfejsa. N em a podrške za pravljenje Javinih jezičkih kon-
strukcija, pa Javin izvorni kod m orate da generišete pom alo prim itivnim m etodam a
p r in t( ) i p rin tln ( ). To znači da m orate paziti na to da zatvorite sve zagrade koje ste otvo-
rili i da vaš kod m ora biti sintaksički ispravan.
M etodu process( ) poziva alatka apt kojoj treba proizvodna m etoda da bi napravila
odgovarajući 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 pomoću 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 obrađuje) i objekat tipa A nnotationProcessorEnvironm ent koji sm o već pro-
puštali 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
pronašao i da li podržavate sve opcije specificirane na kom andnoj liniji. Naročito je važna
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 izaći, a da ništa 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 ugrađena u kom entar Exec na početku 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 pročitavši 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.
Vežba 2: (3) Program u za izdvajanje interfejsa dodajte podršku za deljenje.

Upotreba obrasca Visitor sa alatkom apt


O brada anotacija um e da postane teška. U gornjem prim eru im am o relativno jednosta-
van procesor anotacija koji tum ači sam o jednu anotaciju, pa ipak sm o m orali prilično da
ga usložnimo. Da se složenost ne bi preterano povećavala s dodavanjem više anotacija i
više procesora, API m irror im a klase koje podržavaju projektni obrazac Visitor (Pose-
tilac). Visitor je jedan od klasičnih projektnih obrazaca iz knjige Design Patterns (Projekt-
ni obrasci) koju su napisali G am m a i dr, a opširnije objašnjenje nalazi se i u knjizi
T h in kin g in Patterns.
Visitor prolazi kroz struk tu ru podataka ili kolekciju objekata i obavlja ođređenu ope-
raciju na svakome od njih. S truktura podataka ne m ora biti uređena, a operacija koju
obavljate na svakom objektu m ora biti specifična za njegov tip. Tim e se operacije razdva-
jaju od sam ih objekata, što znači da m ožete dodavati nove operacije, a da ne dodajete m e-
tode u definiđje klasa.
Visitor je podesan za obradu anotacija zato što Java klasu možete 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 posećena. Dakle, možete realizovati odgovara-
juće ponašanje 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 nutrašnje klase. O bratite pažnju 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 statično polje in-
terfejsa DeclarationVisitor, što je objekat tipa DeclarationV isitor koji ne radi ništa.
PosetilacZaPravljenjeTabela nasleduje ldasu Sim pleDeclarationVisitor i redefiniše
m etode visitC lassD eclaration( ) i visitFieldD ecIaration( ). SimpleDeclarationVisitor
86 4 Misliti na Javi

je adapter koji realizuje sve m etode interfejsa DeclarationVisitor, pa možete 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 ožda vam ovo izgleda kao kom plikovaniji način rada, ali njim e dobijam o skalabil -
nije rešenje. Za složeniji procesor anotacija, pisanje sam ostalnog procesora na način pret-
hodnog p rim era ubrzo bi postalo previše kom plikovano.
Vežba 3: (2) Program u ProizvodjacProcesoraZaPravljenjeTabela.java dodajte podršku
za više SQL tipova.

Jedinično testiranje pomoću anotacija


Jedinično testiranje (engl. u n it testing) je praksa pravljenja jednog ili više testova za svaku
m etodu klase, kako bi se pravilno ispitalo da li se delovi klase ispravno ponašaju. Najko-
riščenija alatka za jedinično testiranje u Javi je JUnit; u vrem e pisanja ove knjige, JUnit su
ažurirali 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 izvrša-
vanju JUnit ispitivanja. S vrem enom se to svelo, ali će anotacije testiranje još približiti
„najjednostavnijem sistemu za jedinično testiranje koji se može zamisliti".
U verzijam a JUnita pre anotacija, m orali sm o da pišem o zaset>nu klasu za skladištenje
jediničnih testova. Uz anotacije, jedinične testove m ožem o da uključim o u klasu koja se
testira i tim e na m inim um svedemo vreme i tru d pri jediničnom testiranju. Taj pristup
im a i veliku d od atn u prednost što se na taj način m ogu testirati i privatne metode, jedna-
ko lako kao javne.
Pošto je struktu ra za testiranje u ovom prim eru napravljena pom oću anotacija, na-
zvao sam je @Unit. Za najjednostavnije testiranje (koje ćete verovatno najčešće 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 možete dati proizvoljna imena. Ta-
kođe, @Unit ispitnim m etodam a m ožete 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-
govarajuće m etode i polja označite sa @ Unit (o tom e ćete više 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 lakše 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 {
OSIzvršenje.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 jedinično testirane (engl. @ U nit tcsted ili utiit tested) m oraju biti
sm eštene 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 jedinične testove.
S truktura se stara da one ne prim e ulazne argum ente i da vrate boolean ili void. Kada bu-
dete sami pisali jedinične testove, sam o utvrdite da li je test uspeo ili ne, tj. da li je vratio
true ili false (za m etode koje vraćaju boolean).
Ako poznajete JUnit, prim etićete i da @Unit daje inform ativnije rezultate - prikazuje
se tekući test (onaj koji se trenutno izvršava) 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 ugrađujete u svoje klase.


Neugrađene testove je najlakše napraviti nasleđivanjem:

/ / : 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 neugrađenih 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 {
OSIzvršenje.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 početku 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 početku. (Ne tvrdim da je to
idealno rešenje, nego sam o pokazujem jed n u od m ogučnosti.)
Za pravljenje neugrađenih testova m ožete 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 neugrađenih 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, pošto se po jedan objekat tipa
AtUnitKompozicija pravi za svaki test.
N ema posebnih m etoda za proveru uspešnosti testiranja kao u JUnitu, ali drugi oblik
m etode @Test om ogućuje da vratite tip void (ili boolean, ako i u ovom slučaju želite da
vraćate tru e ili false). Za testiranje uspešnosti m ožete koristiti Javine naredbe assert koje
treba uključiti indikatorom -ea na java kom andnoj liniji, ali ih @Unit uključuje autom at-
ski. Kao pokazatelj neuspeha m ožete upotrebiti čak i izuzetak. Jedan od ciljeva prilikom
projektovanja alatke @Unit bio je da se količina dođatne sintakse svede na najm anju
m eru, pa su za prijavljivanje grešaka potrebni sam o Javine naredlie assert i izuzeci. Da je
test neuspešan, pokazuje assert koji vrati negativan rezultat ili izuzetak koji potiče iz
ispitne m etode, ali @Unit ni u tom slučaju 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 možemo 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 spešnosti 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 {
OSIzvršenje.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 neugrađenim testovim a čija se uspešnost 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 {
OSIzvršenje.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 ograničenja, izgleda da je način s nasleđivanjem jednostavniji.


Vežba 4: (3) Proverite da li se nov testObjekat pravi pre svakog testa.
Vežba 5: (1) Izmenite gornji p rim er korišćenjem p ristupa s nasleđivanjem .
Vežba 6: (1) Testirajte LinkedList na način prikazan u program u HashSetTest.java.
Vežba 7: ( 1) Izmenite prethodni prim er korišćenjem pristupa s nasleđivanjem.
Za svako jedinično 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 jedinična 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 statičnu m etodu za građenje objekata i prikačite za nju
anotaciju @TestObjectCreate, na ovaj način:

/ / : 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 zvršenj 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 statična i vratiti objekat tipa koji testirate - pro-
gram @Unit će se postarati da to bude ispunjeno.
Katkada će vam zatrebati dodatna polja za podršku jediničnom testiranju. A notaciju
@TestProperty možete upotrebiti za označavanje polja koja se upotrebljavaju sam o za je-
dinično testiranje (da biste mogli da ih uklonite pre isporuke program a klijentu). U na-
rednom p rim eru čitam o vrednosti iz znakovnog niza raščlanjenog 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 određeno 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 " ) ;
OSIzvršenje.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 :
kreće
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 možcte upotrebiti i za označavanje m etoda koje se tokom testiranja


koriste, ali same nisu testovi.
Vodite računa o tom e da uspešnost ovog program a zavisi od redosleda izvršavanja te-
stova, što po pravilu nije dobro.
872 Misliti na Javi

Ukoliko tokom inicijalizacije objekata za testiranje uradite nešto što kasnije treba
počistiti (engl. clean up ), m ožete dodati statičnu m etodu označenu sa @ TestObject-
C leanup koja obavlja čišćenje kada završite testiranje objekta. U sledećem 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 {
OSIzvršenje.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
Pokrećem cleanup
. test2
Pokrećem cleanup
. te st3
Pokrećem cleanup
OK (3 t e s t s )
* ///•-
Iz ispisa rezultata vidite da se m etoda za čišćenje autom atski pokreće nakon svakog te-
stiranja.

Testiranje generičkih tipova alatkom @Unit


G enerički tipovi predstavljaju poseban problem , zato što ne možete „testirati generički"
(uopšteno). Testira se određeni param etar tipa ili skup param etara. Rešenje je jednostav-
no: izvedite klasu za testiranje iz speđficirane verzije generičke 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 ulanćane 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 ( ) ; }
} ///:-

Izvešćemo 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 {
OSIzvršenje.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 )
* ///:-

Jeđini potencijalni nedostatak nasleđivanja jeste to što gubim o m ogućnost pristupanja


privatnim m etodam a klase koja se testira. Ukoliko to predstavlja problem , dodajte toj
metodi m odifikator protected ili napišite neprivatnu @TestProperty m etodu koja poziva
tu privatnu m etodu (potom će <®TestProperty m etoda alatkom AtUnitRemover biti
izbačena iz koda za isporuku, i to ću prikazati u nastavku poglavlja).
Vežba 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.
Vežba 9: (2) Napišite osnovne @Unit testove za HashMap.
Vežba 10: (2) Izaberite neki prim er iz drugih delova knjige i dođajte 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 saopštiti šta želite da testirate, i zato se testovi grupišu u ,,svite“ da bi JUnit
mogao da ih pronade i pokrene.
@Unit jednostavno traži datoteke klasa koje sadrže odgovarajuće anotacije i zatim
izvršava @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 početi 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 teško bez ikakvih dodatnih prepreka, pa pom oću @Unit može postati m nogo
lakše. Zbog toga su veće šanse da ćete zaista početi da pišete testove.
Poglavlje 20: Anotacije 875

Realizacija interfejsa @Unit


Prvo m oram o da definišemo sve tipove anotacija. To su obične oznake bez polja. Oznaka
@Test je bila definisana na početku 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 označeni 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/.đvojičemo anotacije pom oču refleksije da bism o realizovali sistem koji izvršava te-
stiranje. Na osnovu tih informacija program odlučuje kako da napravi test objekte i kako da
ih testira. Zbog anotacija, to se postiže iznenađujuče 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 saopštava 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 pretražiti stablo tekućeg di-
rektorijum a. M ožete zadati i više argum enata, bilo class datoteka (s nastavkom .class ili
bez njega) bilo direktorijum a. Pošto @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 reši kada pronađe class datoteku jeste taj
što se p u n o im e klase (koje obuhvata i ime njenog paketa) ne m ože saznati iz im ena same
class datoteke. Zato se class datoteka m ora analizirati, što nije lako, ali ni nem oguće.9
Dalde, nakon pronalaska .class datoteke ona se prvo otvara, njen binarni sadržaj učitava
i predaje m etodi ClassNam eFinder.thisClass( ). Potom prelazim o u dom en „inženjerin-
ga bajtkoda“, pošto zapravo analiziram o sadržaj 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 riše 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 ožem o ulaziti u sve pojedinosti, svaka class datoteka im a odredeni for-
m at, a ja sam parčićim a podataka izvadenih iz toka B yteA rray In p u tS tream pokušao da
dam smislena imena. Veličinu svakog parčeta možete saznati na osnovu dužine učitanog
ulaznog toka. Prim era radi, prva 32 bita svake class datoteke uvek zauzim a „magični broj“
Poglavlje 20: Anotacije 881

Oxcafebabe,10 a sledeća dva shorta su inform acija o verziji. Skladište (engl. pool)
konstanti sadrži konstante za program i zato je prom enljive veličine; sledeći short kazuje
koliki je, da bi m u mogao biti dodeljen niz odgovarajuće veličine. Svaka stavka skladišta
konstanti može biti vrednost fiksne ili prom enljive veličine, pa m oram o da ispitam o
oznaku kojom stavka počinje da bism o shvatili šta s njom da radim o - to je naredba
switch. Ovde nism o pokušavaii da tačno analiziram o sve podatke u class datoteci, nego
sam o da prođem o kroz sve važne parčiće i da ih uskladištim o, pa se nem ojte čuditi što se
dobar deo podataka odbacuje. Inform acije o klasam a skladište se u tabeli className-
Table i tabeli offsetTable. Nakon učitavanja skladišta konstanti m ože se pronaći 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 ože u njoj
p o tra ž iti., što znači da je deo nekog paketa. Klase izvan paketa se zanem aruju. Ukoliko je
klasa u paketu, standardni učitavač klasa učitava klasu m etodom Class.forN am e( ). Sada
se u klasi m ogu potražiti @Unit anotacije.
Tražim o samo tri stvari: @Test m etode koje su uskladištene u listi TestMethods i me-
tode označene sa @TestObjectCreate ili @TestObjectCIeanup. Njih pronalazim o tako
što pozivam o pridružene m etode koje traže anotacije.
Ako se pronađe neka @Test m etoda, ime odgovarajuće klase se štam pa da bi posm a-
trač znao šta se događa i zatim se izvršava svaki test. To znači 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 uspešan ukoliko se nije pojavio
izuzetak (što bi se desilo u slučaju neuspešne naredbe assert ili bilo koje druge vrste izu-
zetka). Ukoliko izuzetak bude generisan, štam paju se pripadajuće inform acije da bi
korisnik video uzrok. U svim slučajevima neuspeha (engl. failure) povećava se sadržaj
brojača neuspeha, a ime neuspešne klase i m etode dodaju se listi failedTests da bi bile pri-
javljene na kraju testiranja.
Vežba 11: (5) Alatki @Unit dodajte anotaciju @TestNote; ona označava prateće napom e-
ne (engl. notes) koje se ispisuju tokom testiranja.

Uklanjanje koda za testiranje


M ada u m nogim projektim a neće sm etati ako kođ za testiranje ostavite u program u koji
se isporučuje (naročito ako sve ispitne m etode označite kao privatne, što m ožete ako že-
lite), u nekim slučajevima m oraćete taj kod da izbacite kako ga ne biste otkrili kupcu ili da
bi verzija koja se isporučuje bila mala.
Za to je potreban inženjering bajtkoda sofisticiraniji od onog koji se može lako napisati.
M eđutim , biblioteka otvorenog koda Javassist11 svodi inženjering bajtkoda u dom en m o-
gućeg. 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 afiću 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 učinite, 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 skladišta
ClassPool, slično kao što učitavač klasa (engl. class loadcr) i m etoda Class.forN am e( )
učitavaju klase u JVM.
CtClass sadrži bajtkodove objekta klase i om ogućuje 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 traže 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 složenije od uklanjanja m etoda.
Polja nije dovoljno prosto ukloniti, pošto u vezi s njim a m ože biti statičnih operacija ini-
cijalizađje. Zato gornja verzija koda uklanja sam o @Unit m etode. Redovno na Web
lokaciji biblioteke Javassist tražite nove verzije; trebalo bi da i uklanjanje polja jed n o m
bude moguće. U m eđuvrem enu, imajte u vidu da spoljna m etoda za testiranje prikazana
u program u AtUnitSpoljniTest.java om ogućuje uklanjanje svih testova tako što se obriše
class datoteka koju pravi kod 7.a testiranje.

Sažetak
Anotacije su dobrodošao dodatak Javi. O ne su stru k tu riran način dodavanja m etapoda-
taka kodu, zajemčeno bezbedan u pogledu tipova, a pri tom kod ne čini nečitljivim i za-
petljanim . O ne mogu pom oći 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 ugrađenih anotacija. To znači da ćete sam i pisati anotacije i
njim a pridruženu logiku, ukoliko ne p ronađete neku biblioteku s gotovim anotacijam a.
A latkom ap t m ožete u jedn om koraku prevesti novogenerisane datoteke i olakšati proces
autom atskog prevođenja i pakovanja ( b uilda ), ali tren u tn o u API-ju m irror im a malo
više od osnovnih funkcija za identifikovanje elem enata definicija Java klasa. Kao što ste
videli, za inženjering bajtkoda m ože se upotrebiti Javassist ili kod koji ćete eventualno
sami napisati.
Situacija će se svakako poboljšati u ovom pogledu i proizvođači API-ja i struktura
počeće da isporučuju anotacije kao deo svojih kom pleta alatki. Kao što ste mogli da
zaključite nakon upoznavanja sa sistem om @Unit, veom a je verovatno da će anotacije
znatno prom eniti naš doživljaj program iranja na Javi.
Rešenja odabranih vežbi data su u elektronskom dokumentu The Thinking in Java Annotatcd Solu-
tion Guide, koji se može kupiti na lokaciji www.MindVicw.com.
Paralelno izvršavanje
D osad ste učili 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 ožem 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 rešavati paralelnim
izvršavanjem više delova program a, tako da izgleda da se ti delovi izvršavaju istovrem eno
ili to zaista i jeste tako, ukoliko računar ima više procesora ili procesor im a više jezgara.
Paralelnim program iranjem m ože se značajno ubrzati izvršavanje program a, dobiti
lakši m odel za projektovanje određenih vrsta program a, ili i jedno i drugo. M eđutim ,
izveštiti se u teoriji i tehnikam a paralelnog program iranja predstavlja viši stepen spo-
sobnosti o d svega što ste dosad saznali u ovoj knjizi. To je tem a za srednji ili napredni nivo
stručnosti. Ovo dugačko poglavlje može poslužiti 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 izvršavanju nastaje kada zadaci koji
se paralelno izvršavaju počnu m edusobno da se om etaju. To se može desiti na tako sup-
tilan i slučajan način da je verovatno pošteno reči kako je paralelno izvršavanje ,,u teoriji
determ inističko, ali u praksi stohastičko“. D rugim rečima, ukoliko se pažljivo radi i pre-
gleda kod, m oguće je napisati program e za paralelno izvršavanje koji rade ispravno.
M eđutim , u praksi je m nogo lakše pisati program e za paralelno izvršavanje koji sam o
naizgled rade, a u određenim okolnostim a zakazuju. Te okolnosti se ne m oraju dogoditi
ili se m ogu događati toliko retko da ih tokom testiranja uopšte ne vidite. Zapravo, m ožda
ne m ožete da napišete kođ za testiranje koji generiše okolnosti u kojim a vaš program za
paralelno izvršavanje otkazuje. Otkazivanja se često dešavaju tek povrem eno i zato se o
njim a sazna tek iz žalbi korisnika. To je jedan od najjačih razloga za proučavanje para-
lelnog izvršavanja: ako ga preskočite, verovatno će vam se osvetiti.
Dakle, paralelno izvršavanje je prepuno opasnosti, i ako vas to plaši, tako i treba. Iako
je Java SE5 đonela znatna poboljšanja u paralelnom izvršavaniu, i dalje nem a zaštite koja
bi vam ukazala na grešku, kao što su provera u vreme prevođenja ili izuzeci koji se pro-
veravaju. S paralelnim izvršavanjem m orate sami da se nosite, i pouzdan višenitni kod
možete da pišete na Javi sam o ako ste istovrem eno sumnjičavi i agresivni.
Ima mišljenja da je paralelno izvršavanje preteška tem a za knjigu čiji je stručni nivo
početni. Ti ljudi sm atraju da je paralelno izvršavanje zasebna tem a koja se može obraditi
nezavisno, a nekoliko slučajeva koji se pojavljuju u svakodnevnom program iranju (kao
što su grafička korisnička okruženja) mogu biti obrađeni posebnim idiom im a. Zašto
načinjati toliko složenu tem u ako se to može izbeći?
Eh, kada bi to bila istina. Nažalost, ne biram o mi kada će se u našim Java program im a
pojaviti niti. To što sami niste otpočeli nit ne znači da možete izbeći pisanje višenitnog ko-
da. Na prim er, na Javi se najčešće pišu Web sistemi, a osnovna klasa Web biblioteke, servlet,
po prirodi je višenitna - i m ora da bude pošto Web serveri često imaju više procesora, a
paralelno izvršavanje je idealan način upotrebe tih procesora. Koliko god da servlet izgleda
jednostavno, m orate razum eti paralelno izvršavanje da biste servlete ispravno koristili.
886 Misliti na Javi

Isto važi i za program iranje grafičkih korisničkih okruženja, kao što ćete videti u sledećem
poglavlju. Iako biblioteke Swing i SWT im aju m ehanizm e za bezbednost niti, teško je shva-
titi kako da ih ispravno upotrebljavate ako ne razum ete paralelno izvršavanje.
Java je višenitni jezik i problem i s paralelnim izvršavanjem postoje, znali vi to ili ne.
Zato postoji m nogo Java program a koji rade ili slučajnim sticajem okolnosti ili tek veći
deo vrem ena, i povrem eno m isteriozno zakazuju zbog neotkrivenih grešaka u para-
lelnom izvršavanju. Katkada su ta zakazivanja dobroćudna, ali um eju da prouzrokuju
gubljenje vrednih podataka, i ako barem niste čuli za problem e s paralelnim izvršava-
njem, mislićete da je problem u svemu pre nego u vašem softveru. Ta vrsta problem a
m ože izroniti ili se pojačati kada se program prem esti na višeprocesorski računar. Nakon
što proučite ovo poglavlje, trebalo bi da znate kako prividno ispravni program i mogu
zbog paralelnog izvršavanja da ispolje neispravno ponašanje.
Kada počnete da se bavite paralelnim program iranjem , kao da ste otišli u stranu zemlju
da učite njen jezik ili barem p o tp u n o nov skup jezičkih pojm ova. Naučiti paralelno pro-
gram iranje jednako je zahtevno kao naučiti objektno orijentisano program iranje. Ako se
potrudite, shvatićete osnovni m ehanizam , ali za pravo ovladavanje tim predm etom po
pravilu je potrebno dobro zagrejati stolicu. Iz ovog poglavlja steći ćete osnovno znanje o
paralelnom izvršavanju, pa ćete razum eti pojmove i moći ćete da pišete um ereno složene
višenitne program e. Budite svesni da je veoma lako steći neosnovano veliko poverenje u
svoje sposobnosti za paralelno program iranje. Moraćete da pročitate knjige posvećene
isključivo toj temi ukoliko se odlučite za pisanje iole složenijih višenitnih programa.

Svi aspekti paralelnog izvršavanja


Paralelno program iranje je teško shvatljivo zato što se njim e istovrem eno m ora rešiti više
problem a. Uz to, postoji više načina realizacije paralelnog rada, a da nam nije jasno koji
problem se rešava kojim pristupom . (Često su nejasne i granice izm eđu njih.) Zato mo-
rate da shvatite sve problem e i specijalne slučajeve da biste efikasno upotrebljavali
paralelno izvršavanje.
Probleme koje rešavamo paralelnim izvršavanjem grubo delim o na „brzinu izvrša-
vanja“ i „upravljivost projekta“.

Brže izvršavanje
Spočetka problem zvuči jednostavno: ako želite da se program izvršava brže, podelite ga na
niti koje izvršavaju zasebni procesori. Paralelno izvršavanje je osnovna alatka višeproce-
sorskog program iranja. Pošto M urov zakon sve ređe deluje (barem za obična integrisana
kola), sredstvo za povećanje brzine postaju procesori s više jezgara, a ne brža integrisana
kola. Da biste naterali program e da rade brže, m oraćete naučiti da iskoristite te dodatne
procesore, i to je jedna od stvari koje će vam paralelno izvršavanje omogućiti.
Ako imate računar s više procesora, njim a se može dodeliti više zadataka (na isto-
vrem eno izvršavanje), što um e znatno da poboljša protok. Tako često rade snažni više-
procesorski Web serveri; oni procesorim a raspoređuju veliki broj korisničkih zahteva u
program u koji dodeljuje jednu nit po zahtevu.
Poglavlje 21: Paraielno izvršavanje 887

M eđutim , paralelno izvršavanje često poboljšava perform anse program a koji se


izvršavaju na je d n o m procesoru.
Ovo zvuči neverovatno ili barem neočekivano. Razmislite: paralelni program koji se
izvršava na jednom procesoru trebalo bi da im a veće režijske troškove (slabije
perform anse) nego u slučaju da se svi delovi program a izvršavaju sekvencijalno, jer se
m o ra platiti dodatno prebacivanje konteksta (prelazak s jednog zadatka na drugi). Pri
površnom razm atranju, reklo bi se da je jeftinije sve delove program a izvršavati kao jedan
zadatak i uštedeti troškove prebacivanja konteksta.
Ali uzm ite u obzir blokiranje. Ukoliko se jedan zadatak u vašem program u ne izvršava
zbog neke okolnosti izvan kontrole program a (obično je to U/I), kažem o da taj zadatak ili
nit blokira izvršavanje program a. Ako nem a paralelnog izvršavanja, ceo program se zau-
stavlja dok se spoljne okolnosti ne prom ene. S druge strane, ako je program napisan za
paralelno izvršavanje, ostali zadaci u program u m ogu se izvršavati dok je jedan zadatak
blokiran, pa program u celini nastavlja rad. Zapravo, sa stanovišta perform ansi, nem a
smisla pisati program za paralelno izvršavanje na jednoprocesorskom računaru ukoliko
nijedan od njegovih zadataka ne m ože da zablokira izvršavanje program a.
Perform anse jeđnoprocesorskih računara često poboljšavamo program im a kojitna
upravljaju događaji. Više niti se koristi baš zato da bi se napravilo korisničko okruženje
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 neće zaboraviti da proveri šta radi korisnik. Ako nema
paralelnog izvršavanja, korisničko okruženje koje brzo reaguje ostvaruje se sam o kada svi
zadaci periodično proveravaju korisnikov unos. O dređenu brzinu reagovanja program
jem či tako što pravi zasebnu nit izvršavanja koja reaguje na korisnikov unos, iako će ta nit
gotovo neprestano biti blokirana.
Program treba da nastavi izvršavanje svojih operacija i istovrem eno da vrati kontrolu
korisničkom okruženju kako bi mogao da reaguje na radnje korisnika. Ali obična m etoda
ne može da se izvršava i da istovrem eno vrati kontrolu nad procesorom ostatku progra-
ma. To zvuči nem oguće - kao da procesor treba da bude na dva mesta u isto vreme, ali
upravo to i jeste iluzija koju pruža rad s više niti. (U slučaju višeprocesorskih sistema, to
i nije iluzija.)
Veoma jednostavan način da se na nivou operativnog sistema realizuje paralelno
izvršavanje jeste upotreba procesa. Proces je svaki sam ostalan program koji se izvršava i
ima vlastiti adresni prostor. Višeprogramski (engl. m ultitasking) operativni sistem može
da izvršava više procesa (program a) istovremeno, a da pri tom izgleda kao da se svaki
izvršava sam za sebe; to se postiže raspodelom procesorskog vrem ena na sve tekuće pro-
cese. Procesi su veoma privlačni zato što ih operativni sistem najčešće 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 izvršavanje - kao što je Javin - dele resurse (m em oriju
i U/I) m eđu nitim a, pa je osnovna poteškoća pisanja višenitnih program a koordinacija
upotrebe tih resursa u različitim zadacima vođenim 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 pišem knjigu, redov-
no pravim više redundantnih rezervnih kopija tekućeg stanja knjige. Jednu kopiju napra-
vim u lokalnom direktorijum u, dru gu na fleš uređaju, treću 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 sadrži broj ver-
zije i zatim kopira. Spočetka sam sve kopije pravio sekvencijalno (čekao da se prethodno
kopiranje završi pa da otpočnem sledeće). Potom sam shvatio da operacije kopiranja ne
traju jednako, jer se U/I brzine m edijum a razlikuju. Pošto sam im ao višeprogram ski
operativni sistem, svaku operaciju kopiranja m ogao sam da započnem kao zaseban pro-
ces i pustim ih da se izvršavaju paralelno - tako se ubrzavalo izvršavanje celog program a.
D ok je jedan proces blokiran, drugi m ogu da rade.
To je idealan prim er paralelnog izvršavanja. Svaki zadatak se izvršava kao proces u
sopstvenom adresnom prostoru, p a zadaci ne m ogu da utiču jedni na druge. Još je važnije
to što zadaci nem aju potrebe da kom uniciraju, pošto su p o tp u n o nezavisni. O perativni
sistem se stara o svim pojedinostim a koje obezbeđuju ispravno kopiranje datoteka. Stoga
nem a rizika i dobijam o brži program , zapravo besplatno.
Neki zagovaraju procese kao jedini razum an p ristu p paralelnom izvršavanju,1 ali je
nažalost broj procesa po pravilu ograničen, a njihovi režijski troškovi veliki. Zato se pri-
m enom procesa ne mogu rešiti svi zadaci iz spektra paralelnog izvršavanja.
Neki program ski jezici m eđusobno izoluju zadatke koji se izvršavaju paralelno.
O bično ih nazivamo fu n kcijski jezici, jer u njim a poziv funkcije ne prouzrokuje sporedne
uticaje (te ne može ni da utiče na druge funkcije) i stoga se m ože izvršavati 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 vašeg program a m ora intenzivno da
se koristi paralelno izvršavanje, a pri pisanju tog dela nailazite na velike problem e, raz-
mislite o ideji da taj deo program a napišete na nam enskom jeziku za paralelno izvrša-
vanje kao što je Erlang.
U Javi je pristup tradicionalniji - podrška za višenitni rad dodata je na gotov
sekvencijalni jezik.2 Umesto da se ,,love“ spoljni procesi u višeprogram skom operativnom
sistemu, niti prave zadatke u n u ta r istog procesa koji predstavlja program koji se izvršava.
Jedna od prednosti koje se pri tom e postižu jeste nezavisnost od operativnog sistema, što
je bio važan cilj prilikom projektovanja Jave. Na prim er, pre verzije OS X operativnog
sistema M acintosh (prilično važna publika za prve verzije Jave) nisu podržavale višepro-
gramski rad. Da višenitni rad nije dodat u Javu, Java program i za paralelno izvršavanje ne
bi mogli da se izvršavaju na M acintoshu i njem u sličnim platform am a, čim e bi se narušio
zahtev „napiši jednom/izvršavaj 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 ršav 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 više ne ističe ta k o g la sn o . D a iro n ija b u d e veća,
je d a n o d ra z lo g a što k o n c e p t „ n a p iši 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 izvršavanje 889

Poboljšan dizajn koda


Program koji izvršava više zadataka na računaru s jednim procesorom ipak u svakom tre-
n utku radi sam o jedan posao, pa teorijski m ora biti m oguće d a se isti program napiše bez
svih tih zadataka. M eđutim , paralelno program iranje daje važnu organizacionu pred-
nost: dizajn program a se m ože znatno pojednostaviti. Neke vrste zadataka, kao što je si-
m ulacija, teško je rešiti bez paralelnog program iranja.
Večina ljudi je videla barem jedan oblik simulacije, bilo kao računarsku igricu ili raču-
narski generisanu anim aciju u filmovima. Simulacije po pravilu obuhvataju m nogo ele-
m enata u interakciji, svaki sa „svojom pam eću“. M ada se svako m ože uveriti da u
raćunaru s jednim procesorom svaki elem ent sim ulacije izvršava taj jedini procesor, sa
stanovišta program iranja m nogo je lakše praviti se da svaki elem ent sim ulacije im a sop-
stveni procesor i da predstavlja nezavisan zadatak.
Potpuna simulacija često sadrži veom a veliki broj zadataka, zato što svaki elem ent si-
m ulacije m ože da dejstvuje nezavisno - to važi i za vrata i stene, ne sam o za duhove i ča-
robnjake. Višenitni sistemi često imaju relativno mali broj d o stu p n ih niti, katkada reda
veličine nekoliko desetina ili stotina. Taj broj m ože da se m enja nezavisno od kontrole
program a - m ože zavisiti od platform e, ili u slučaju Jave, od verzije JVM-a. Kad koristite
Javu, obično niožete pretpostaviti da broj dostupnih niti neće biti dovoljan da svakom
elem entu velike simulacije date sopstvenu nit.
Ovaj problem se obično rešava koopcrativrtim višenitnim program iranjem (engl. coope-
rative m ultithreading). Java upotrebljava niti predupredno (engl. p rcem p tive ), što znači da
mehanizam raspoređivanja svim nitim a dodeljuje procesorsko vreme, periodično prekida
svaku nit i prebacuje kontekst na drugu nit, tako da svaka nit dobija um erenu količinu vre-
mena za izvršavanje 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 prepuštanje kontrole. Prednost kooperativnog sistema je dvostruka: preba-
civanje konteksta je obično mnogo jeftinije nego u preduprednom sistemu, i teorijski
nema ograničenja broja nezavisnih zađataka koji se m ogu izvršavati istovremeno. Kada si-
mulacija ima m nogo elemenata, ovo može da bude idealno rešenje. Imajte u vidu da neki
kooperativni sistemi ne raspoređuju zadatke po procesorim a, što je veliko ograničenje.
S druge strane, paralelno izvršavanje je veom a koristan m odel - zato što se ono upravo
i dešava - u radu sa savrem enim sistemima koji razm enjuju p o ru ke i m ogu obuhvatati
m nogo nezavisnih računara razbacanih u mreži. U tom slučaju, svi procesi se odvijaju
nezavisno jedan od drugog i nem a m ogućnosti čak ni za deljenje resursa. M eđutim , 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 neodgovarajuće vreme. Čak i ako nem ate
nam eru da u neposrednoj budućnosti 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 način pravljenja distribuiranih sistema.
Paralelno program iranje nam eće troškove, m eđu kojim a i troškove zbog složenosti, ali
njih po pravilu opravdavaju poboljšanja 11 dizajnu program a, uravnoteženom korišćenju
resursa i lakoći upotrebe. U opšte uzev, niti om ogućuju pravljenje labavije povezane
strukture program a, inače bi delovi program a m orali stalno da obraćaju pažnju na zadat-
ke koje obavljaju niti.
89 0 Misliti na Javi

Osnove višenitnog programiranja


Paralelno program iranje om ogućuje deljenje program a na zasebne delove koji se izvr-
šavaju nezavisno. U višenitnom program u, svaki o d tih zasebnih zadataka (nazivaju ih i
podzadaci) izvršava jedna n it izvršavanja (engl. thread o f execution). N it je pojedinačan
sekvencijalni tok kontrole u nutar nekog procesa. Proces m ože da sadrži više zadataka koji
se paralelno izvršavaju, ali program se piše 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 olakšica za program iranje koja pojednostavljuje istovrem eni rad
s nekoliko operacija u istom program u. Niti om ogućavaju 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 izvršavanje program a na računaru s više procesora. Ali, jedna od velikih prednosti ko-
rišćenja niti leži baš u tom e da ne m orate misliti o hardveru na kom e će se program
izvršavati, pa u kodu ne treba praviti varijante za jedan ili više procesora. Stoga su niti
sredstvo za pravljenje prenosivih program a koji se sami prilagođuju platform i - ako se
program izvršava presporo, lako ćete ga ubrzati dodavanjem procesora u računar. Upo-
treba višeprogram skog (engl. m ultitasking) operativnog sistema i istovrem eno izvrša-
vanje više niti unutar jednog program a (engl. m ultithreading) predstavlja najbolji način
da iskoristite višeprocesorski računar.

Definisanje zadataka
Nit izvršava jedan zadatak, pa m oram o imati načina da taj zadatak opišemo. Tome služi
interfejs R unnable. Zadatak ćete đefinisati kada realizujete R unnable i napišete 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 važi 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 ršav 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 izvršava
sve d o k se n e z a b lo k ira ili se n e zav rši. 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 ršav 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 izvršavanje 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 pripadajućem identifikatoru id. Uz njega je rezervisa-
na reč final zato što ne očekujem o da će se p rom eniti nakon što bude inicijalizovan.
M etoda r u n ( ) obično im a neku vrstu petlje koja se izvršava sve dok zadatak ne posta-
ne nepotreban, pa m orate da utvrdite uslov za izlazak iz nje. Jedna m ogućnost bi bila da
jednostavno izađete (vratite se) iz m etode r u n ( ). M etodu r u n ( ) često pišu u obliku bes-
konačne petlje, što znači da će se ona izvršavati u nedogled ako je nešto ne prekine. (U na-
stavku poglavlja saznaćete kako da bezbedno okončate zadatak.)
Poziv statičke 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 važne 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 poveća-
va verovatnoću da će doći do prebacivanja s jednog zadatka na drugi.
U narednom prim eru, zadatkovu m etodu r u n ( ) ne izvršava 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


ništa osobito - tim e se ne dobija naročita sposobnost višenitnog rada. N ju ćete postići
eksplicitnim dodeljivanjem zadatka nekoj niti.

Klasa Thread
Runnable objekat se pretvara u zadatak koji se izvršava najčešće tako što objekat
prosledite konstruktoru klase Thread. U sledećem prim eru ćem o pom oću klase Thread
pokrenuti izvršavanje 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 pokreće zadatak u novoj niti. Mada izgleda kao da s ta r t( ) poziva neku m etodu
koja se izvršava dugo, iz ispisa rezultata možete videti - poruka „Čekamo na Lansiranje“
ispisuje se pre dovršenja odbrojavanja - da se m etoda s ta r t( ) vraća brzo. U stvari, pozvana
je m etoda L ansiranje.run( ) koja se još nije završila, ali pošto se Lansiranje.run( ) izvršava
u drugoj niti, u metodi m a in ( ) i dalje m ožem o da izvršavamo ostale operacije. (Ta spo-
sobnost nije ograničena na nit m etode m a in ( ) - svaka nit m ože da pokrene druge niti.)
DakJe, program izvršava dve m etode istovrem eno - m a in ( ) i Lansiranje.run( ). Kod m e -
tode r u n ( ) izvršava se istovrem eno sa ostalim nitim a program a.
Lako je dodati još niti za izvršavanje drugih zadataka. U narednom prim eru vidite sve
zadatke u uzajam no usklađenom izvršavanju: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 više n iti


k o je p ra v e n iti za Lansiranje, v iše 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 izvršavanje 893

Ispis rezultata pokazuje da je izvršavanje raznih zadataka izmešano kako se procesor


prebacuje s jedne niti na drugu. To prebacivanje autom atski kontroliše m ehanizam za
raspodelu vrem ena nitim a. Ako računar im a više procesora, m ehanizam za raspodelu če
te niti (za korisnika nevidljivo) raspodeliti procesorim a.6
Rezultati jednog izvršavanja ovog program a neće biti isti kao rezultati drugog, pošto
m ehanizam za raspodelu procesorskog vrem ena nitim a nije determ inistički. U stvari,
različite 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 dešavalo da nit 1
završi svoju petlju, zatim nit 2 prođe kroz sve svoje petlje itd. To je bilo isto kao poziv ru-
tine koja sekvencijalno izvršava jednu petlju za drugom , sem što je pokretanje niti
skuplje. Kasnije su JDK bolje delile procesorsko vreme, pa su sve niti bivale redovnije
opsluživane. Sun po pravilu nije ni spom injao takve prom ene u ponašanju JDK, pa ne
možete računati na bilo kakvo dosledno ponašanje višenitnog m ehanizm a. Najbolje će
biti da prilikom pisanja višenitnog koda ne pretpostavljate ništa o ponašanju niti.
Kada funkcija m a in ( ) napravi niti (objekte klase T h read ), ona nigde ne p am ti njihove
reference. O bični objekti bi bili proglašeni za smeće, ali to ne važi za niti. Svaka nit se ,,re-
gistruje“ tako da negde postoji njena referenca i skupljač smeća ne može da je ukloni dok
zadatak ne izađe iz svoje m etode r u n ( ) i ne um re. Iz ispisa rezultata vidite da se zadaci
zaista izvršavaju sve do svog završetka. Dakle, svaka nit pravi zasebnu nit izvršavanja koja
postoji i nakon završetka m etode s t a r t ( ).
Vežba 1: (2) Realizujte interfejs R unnable. U m etodi r u n ( ) ispišite neku poru k u i zatim
pozovite y ie ld ( ). Ponovite to trip u t i zatim izađite (vratite se) iz m etode r u n ( ). U kon-
struktor stavite p o ru ku koja se ispisuje prilikom pokretanja, a odgovarajuću poruku
ispišite i kada se zadatak izvrši. Napravite više takvih zadataka i izvršite ih pom oću niti.
Vežba 2: (2) Po uzoru na program genericki/Fibonacci.java, napišite zadatak koji
proizvodi sekvencu od n Fibonaccijevih brojeva, pri čem u se n prosleđuje konstruktoru
zadatka. N apravite više takvih zadataka i izvršite ih pom oću niti.

Upotreba izvršilaca (interfejsa Executor)


Java SE5 u paketu jav a.u til.co n c u rren t sadrži klasu Exccutors , tj. izvršioce koji pojedno-
stavljuju paralelno program iranje tako što iniciraju izvršavanje niti (objekata tipa
T h read ) i upravljaju njime. Izvršioci obezbeđuju sloj indirekcije izm eđu klijenta i izvrša-
vanja zadatka; um esto da klijent izvršava zadatak neposredno, njega izvršava posrednički
objekat, tj. izvršilac (engl. executor). Klasa E xecutors om ogućuje da upravljam o izvrša-
vanjem asinhronih zadataka, a da ne m oram o eksplicitno da upravljam o životnim ciklu-
som niti. Upravo su izvršioci preporučeni način pokretanja zadataka u Javi SE5/6.
U m esto da u program u JosO snovaN iti.java niti pravim o eksplicitno, m ožem o pustiti
izvršioca da se stara o svemu. Objekat tipa L ansiranje um e da izvrši određeni zadatak;
kao i projektni obrazac C o m m a n d (Kom anda), on eksponira sam o jednu m etodu za
izvršavanje. Interfejs ExecutorService (koji proširuje interfejs E xecutor tako što dodaje
m etode za ceo životni ciklus usluge, što znači da zna npr. i da se ugasi - engl. shutdow n)
um e da napravi kontekst podesan za izvršavanje 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 pažnju
na to da se objekat tipa ExecutorService pravi statičkom m etodom klase Executors; ona
određuje koja će vrsta izvršioca 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 izvršiocem m ožete da napravite sve zadatke u sistemu i da
svima njim a upravljate.
Poziv m etode s h u td o w n () sprečava dodeljivanje novih zadataka tom izvršiocu. Te-
kuća nit - u ovom slučaju, ona koja izvršava m a in ( ) - nastaviće da izvršava sve zadatke
prijavljene pre poziva m etode s h u td o w n ( ). Program će prekinuti izvršavanje čim se
završe svi zadaci u tom izvršiocu.
C achedT hreadP ool iz prethodnog prim era lako m ožete zam eniti nekom drugom vr-
stom izvršioca. Za izvršavanje prijavljenih zadataka, F ixedT hreadPool koristi ograničen
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 izvršavanje 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 početku


- čim e se broj niti ograničava. Štedi se vreme, zato što se niti ne prave stalno za svaki
zadatak. Pored toga, u sistem u vođenom događajim a, rukovaoci događajem (engl. even t
handlers) kojim a zatrebaju niti m ogu biti odm ah opsluženi tako što jednostavno uzim aju
niti iz grupe (engl. pool). Ne m ožete preterati s korišćenjem resursa zato što Fixed-
ThreadPool upotrebljava ograničen broj objekata tipa Thread.
Im ajte u vidu da se postojeće niti u svim grupam a autom atski ponovo upotrebljavaju
čim to postane moguće.
Iako ću ja u ovoj knjizi upotrebljavati neograničene grupe niti (CachedThreadPooI),
razmislite o korišćenju ogranićenih grupa (FixedThreadPooI) u kodu koji se isporučuje
kupcim a. CachedThreadPool obično pravi onoliko niti koliko m u je potrebno tokom
izvršavanja program a, a zatim prestaje da pravi nove niti zato što reciklira stare; stoga je
najbolje da on bude izabran kao prvi izvršilac. Na FixedThreadPool treba da pređete tek
ako prethodni pristup napravi problem e.
SingleThreadExecutor je kao FixedThreadPool čija je veličina ograničena na jednu
nit. To je podesno za kontinualne (dugotrajne) zadatke, kao što je zadatak koji osluškuje
priključke ulaznih utičnica. Prikladno je i za kratke zadatke koje želite da izvršite u niti
- recimo, male zadatke koji ažuriraju lokalni ili udaljeni dnevnik (engl. log) ili za nit koja
raspoređuje događaje.
Kada se izvršiocu tipa SingIeThreadExecutor prijavi više zadataka, oni se smeštaju u
red za čekanje i svaki zadatak se poziva tek kada prethodni p o tp u n o završi rad, a svi ko-
riste istu nit. U narednom prim eru, videćete da svaki zadatak, po redosledu prijavljivanja,
biva dovršen pre otpočinjanja sledećeg. Dakle, SingleThreadExecutor serijalizuje zadat-
ke koji m u se prijave i održava sopstveni (skriveni) red zadataka koji čekaju na izvršenje.

//: 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 ršio 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 ršav 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 više niti izvršava zadatke koji koriste sistem
datoteka. Za te zadatke m ožete u potrebiti izvršilac tipa SingleThreadExecutor koji jem či
da se u svakom trenutku u svakoj niti izvršava sam o po jedan zadatak. U tom slučaju ne
m orate da se bavite sinhronizovanjem deljenog resursa (a ni sistem datoteka nećete
zabrljati u m eduvrem enu). Ponekad je bolje rešenje sinhronizovanje resursa (o čem u ćete
više saznati u nastavku poglavlja), ali SingleThreadExecutor om ogućuje da preskoćite
pravilnu koordinaciju kada želite sam o da napravite neki prototip. Serijalizacijom
zadataka možete da uklonite potrebu za serijalizacijom objekata.
Vežba3: (1) Ponovite vežbu 1 s raznim vrstam a izvršilaca predstavljenim u ovom odeljku.
Vežba 4: ( 1) Ponovite vežbu 2 s raznim vrstam a izvršilaca predstavljenim u ovom odeljku.

Dobijanje povratnih vrednosti od zadataka


Zadatak koji realizuje interfejs Runnable može da obavi posao, ali ne m ože da vrati re-
zultat. Ukoliko želite da zadatak vrati neki rezultat kada završi s radom , realizujte interfejs
Callable, a ne Runnable. Callable je novina u Javi SE5; to je generički 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 ZađatakSRezultatom 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 izvršavanje 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 određeni tip re-
zultata koji vraća taj objekat tipa Callable. O bjekat tipa Future možete ispitivati meto-
dom isD o n e( ) kako biste videli da li je završio rad. Kada zadatak završi s radom i ima
rezultat, pozovite m etodu g e t( ) da biste ga saznali. Ukoliko g e t( ) pozovete bez prethod-
ne provere završenosti m etodom isD o ne( ), g e t( ) će se blokirati sve dok rezultat ne bude
sprem an. g e t( ) m ožete pozvati i uz vrem ensko odlaganje (engl. tim eout), ili pozovite
isD o n e( ) kako biste videli da li je zadatak završio 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 ,,pozivajuće“ m etode
koje izvršavaju kolekcije Callable objekata.
Vežba 5: (2) Izmenite vežbu 2 tako da zadatak realizuje interfejs Callable i sabira sve Fi-
bonaccijeve brojeve. Napravite nekoliko zadataka i prikažite rezultate.

Spavanje
M etodom sleep ( ) prekidate (blokirate) izvršavanje zadatka na odredeno vreme. Ako u
klasi Lansiranje zam enite poziv m etode y ie ld ( ) pozivom m etode sleep( ), dobićete 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 određeno 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 ) ;
/ / Način 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 ože da baci izuzetak Interru p ted E x cep tio n koji se hvata u
m etodi r u n ( ). Pošto 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 poboljšava čitljivost tako što om ogućuje
zadavanje vrem enskih jedinica za m etodu s le e p (). T im eU nit se može upotrebiti i za kon-
verzije, što ćete videti u nastavku poglavlja.
U zavisnosti od platform e, m ožda ćete videti da se zađaci izvršavaju po „savršenom
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 izvršava neki
drugi zadatak. To sekvencijalno ponašanje om ogućuje pripadni m ehanizam višenitnog
rada koji se m enja u zavisnosti od operativnog sistema, pa na to ne možete da računate.
Ukoliko m orate da kontrolišete redosleđ izvršavanja zadataka, najbolje ćete proći ako
Poglavlje 21: Paralelno izvršavanje 899

upotrebite kontrole za sinhronizovanje (opisane u nastavku) ili, u nekim slučajevima,


ukoliko uopšte ne budete upotrebljavali niti, nego sami napišete ru tin e koje jedna drugoj
prepuštaju kontrolu po zadatom redosledu.
Vežba 6: (2) N apravite zadatak koji spava tokom nasum ično odabranog vrem ena dugač-
kog izm eđu 1 i 10 sekundi, a zatim prikazuje to vreme spavanja i prekida rad. N apravite
i pokrenite više takvih zadataka. (Neka se njihov broj zadaje na kom andnoj liniji.)

Prioritet
P rioritet niti pokazuje koliko je ona važna m ehanizm u za raspodelu procesorskog vrem e-
na. Iako je redosled kojim CPU izvršava skup niti neodređen, m ehanizam za raspodelu
procesorskog vrem ena daje prednost niti najvišeg p rioriteta koja čeka na izvršavanje. To
ne znači da se niti nižeg prioriteta uopšte ne izvršavaju. (Dakle, prioriteti ne m ogu da
prouzrokuju zastoj u izvršavanju). M ehanizam za raspodelu procesorskog vrem ena ređe
pokreće niti nižeg prioriteta i to je sva razlika.
Gotovo uvek bi sve niti trebalo da se izvršavaju s podrazum evanim prioritetom . Ručno
m enjanje prioriteta najčešće nije opravdano.
Sledi prim er s različitim nivoim a prioriteta. Prioritet postojeće niti čitam o m etodom
g e tP rio rity ( ), a zadajemo (kad god poželim 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 može 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 možete i sami zađati preko
konstruktora; ovde se ono autom atski generiše 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 računa o tom e da m etodom T h read.currentT hread( ) iz tog zadatka možete do-
biti referencu niti (objekta tipa Thread) koja izvršava zadatak.
Iz rezultata vidite da je nivo prioriteta poslednje niti najviši i da su sve ostale niti na
najnižem nivou. O bratite pažnju na to da se prioritet postavlja na početku izvršavanja
m etode r u n ( ); nem a srnisla postavljati ga u konstruktoru, pošto Executor (izvršilac) u
tom trenutku još nije otpočeo zadatak.
U metodi r u n ( ) 100 000 puta se obavlja prilično skupo izračunavanje u form atu pokret-
nog zareza koje obuhvata sabiranje i deljenje tipa double. Promenljiva d je dobila modifi-
kator volatiJe da prevođilac ne bi sprovodio optimizaciju. Ne bism o videli uticaj zadavanja
nivoa prioriteta da nem a tog izračunavanja. (Probajte: pretvorite u kom entar petlju for koja
sadrži double izračunavanja.) Uz izračunavanje, vidite da je mehanizam za raspodelu pro-
cesorskog vrem ena češće pozivao nit najvećeg prioriteta (MAX_PRIORITY). (Tako se po-
našao barcm na Windows XP računaru.) Iako je i ispisivanje na konzoli skupa operacija,
ono ne om ogućuje da vidim o uticaj različitih nivoa prioriteta, pošto se ono ne prekida (u
protivnom bi se konzolni ispis pokvario pri prebacivanju s jeđne niti na drugu), dok se
matem atička izračunavanja m ogu prekinuti. Izračunavanje traje dovoljno dugo da m e -
hanizam za raspodelu procesorskog vrem ena uskače, zamenjuje zadatke i pri tom pazi na
prioritete, tako da se niti visokog prioriteta izvode češće. 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 izvršavanje 901

(onih 10 na ovih 7) neodređeno. Sunov Solaris im a 231nivoa. Jedini pristup koji je prenosiv
na sve platform e jeste držati se MAX_PRIORITY, NORM_PRIORITY i MIN_PRIORITY
prilikom podešavanja nivoa prioriteta.

Prepuštanje
Ukoliko znate da ste uradili ono što je trebalo tokom jednog prolaska kroz petlju u m etodi
r u n ( ), m ehanizm u za raspođelu procesorskog vrem ena nitim a m ožete saopštiti da je te-
kuća n it uradila dovoljno i predložiti m u da procesor dodeli nekom drugom zadatku. Taj
predlog (to i jeste sam o predlog, pošto nem a jem stva da će ga realizacija poslušati) im a
oblik m etode y ie ld ( ). Kada pozovete y ie ld ( ), predlažete prelazak na izvršavanje 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 eđutim , za ozbiljnu kontrolu ili podešavanje aplikacije po
pravilu se ne možete osloniti na y ield ( ). Šta više, često se y ield( ) pogrešno upotrebljava.

Servisne niti
Servisne niti (engl. daem on threads) obezbeđuju neke usluge nam enjene program u u ce-
lini i izvršavaju se u pozadini tokom rada program a, ali ne spadaju u njegove suštinske
delove. Zato se program završava čim završe s radom sve obične niti. Važi i obratno: ako
još ima običnih niti koje se izvršavaju, program nastavlja rad - na prim er, ako postoji nit
koja izvršava m a in ( ).

//: paralelno/JednostavneServisneNiti.java
// Serv is ne niti ne sprečavaju da se program okonča.
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 izvršava u pozadini).
Program nem a šta da radi kada m a in ( ) završi svoj posao, jer se tada ne izvršava ništa
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 izvršavaju u pozadini. (Isprobajte pozive m etode sleep ( ) raznih dužina da biste vi-
deli to ponašanje.)
Program JednostavneServisneNiti.java pravi eksplicitne Thread olijekte kako bi
mogao da ih pretvori u servisne postavljanjem odgovarajućeg indikatora. A tribute niti
(da li je servisna, njen prioritet, ime) napravljenih pom oću izvršioca možete 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 obične ThreadFactory (proizvodne niti) jeste to što ova zadaje
vrednost tru e za status servisne niti. Nov objekat tipa D aemonThreadFactory sada
m ožem o proslediti kao argum ent m etodi Executors.newCachedThreadPool( ):

//: paralelno/DaemonThreadFactory.java
// Pravljenje servisnih niti pomoću ThreadFactory.
import java.uti1 .concurrent.*;
import net.mindview.util.*;
Poglavlj'e 2 1: Paralelno izvršavanje 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 statičkih ExecutorService m etoda za pravljenje preklopljena je tako da pri-


ma objekat tipa ThreadFactory pom oću kojega će praviti nove niti.
M ožem o to podići za jedan stepen više i napraviti uslužnog izvršioca 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 pronašao sam u izvornom kodu klase
Executors.java.
Pozivanjem m etode isD aem on( ) saznaćete da li je data nit servisna (da li se izvršava u
pozadini). Ako se neka nit izvršava u pozadini, to autom atski važi i za sve niti koje ona na-
pravi, kao što se vidi iz sledećeg prim era:

//: paralelno/ServisneNiti.java
// Servisna nit rađa 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 dovrše 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 izvršavanje 905

ServisnaNit uključuje 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 beskonačnu petlju koja poziva m etodu y ield ( ) da bi predala kontrolu
drugim procesima.
Im ajte u vidu činjenicu da servisne niti ne izvršavaju odredbe finally prilikom
okončanja svojih m etoda r u n ( ):

//: pa ra le lno/ServisneNitiNeIzvrsavajuOdređbeFinally.java
// Servisne niti ne izvršavaju 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("Kreće Servi sn aN it A" );
TimeLlnit.SECONDS.sleep(l);
} catch(InterruptedException e) {
p r i n t ("Iz1azak pomoću In te rr up te dE xc ep ti on" );
} finally {
print("0vo treba stalno da se izvršava?");

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:
Kreće ServisnaNitA
* ///:-

Kada pokrenete ovaj program , videćete da se odredba finally ne izvršava, ali ako poziv
m etode s e tD a e m o n () pretvorite u kom entar, videćete da se odredba finally izvršava.
Takvo ponašanje je ispravno, iako ga m ožda ne očekujete na osnovu prethodnih
obećanja u vezi sa odredbom finally. Servisne niti bivaju naglo prekinute kada prestane
izvršavanje poslednje neservisne niti. Dakle, čim m a in ( ) izade, JVM odm ah prekida rad
svih servisnih niti, bez ikakvih form alnosti koje ste m ožda očekivali. Pošto se servisne niti
ne m ogu lepo prekinuti, one su retko kada podesne. Po pravilu su neservisni izvršioci
bolji, pošto se svi zadaci koje izvršilac kontroliše m ogu prekinuti odjednom . Kao što ćete
videti u nastavku poglavlja, u tom slučaju se gašenje odvija pravilno.
Vežba 7: (2) Eksperim entišite s različitim vrem enim a spavanja u program u Servisne-
N iti.java da biste videli šta se dešava.
906 Misliti na Javi

Vežba 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 izađe.
Vežba 9: (3) Izmenite program ProstiPrioriteti.java tako da nam enski proizvođač niti
(klasa ThreadFactory) postavlja prioritete niti.

Varijante programiranja
U dosadašnjim prim erim a su sve klase zadataka realizovale interfejs Runnable. U veoma
jednostavnim slučajevima m oguč je i drugačiji pristup, nasleđivanje neposredno od klase
Thread, što se radi ovako:

//: paralelno/JednostavnaNit.java
// Neposredno nasleđivanje od klase Thread.

public class JednostavnaNit extends Thread {


private int odbrojavanje = 5;
private static int brojacNiti = 0;
public JednostavnaNit() {
// Sačuvaj 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 odgovarajuće T hread
konstruktore. To ime ispisuje to S trin g () nakon što ga dobije od m etode getN am e().
M ožda ćete sresti i idiom objekta tipa R unnable koji sam sobom upravlja:

//: paralelno/Samoupravno.java
// Objekat tipa Runnable koji sadrži sopstvenu nit koja ga izvršava.
Poglavlje 2 1: Paralelno izvršavanje 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 nasleđivanja od klase Thread, sem što je sintaksa nešto
neobičnija. M eđutim , realizacija interfejsa om ogućuje nasledivanje od druge kiase, dok
nasledivanje od klase Thread to ne om ogućuje.
O bratite pažnju 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 atično, pošto bi m oglo početi izvršavanje nekog
drugog zadatka pre nego što ko nstruktor završi svoj posao, što znači da taj zadatak može
da pristupi objektu koji je u nestabilnom stanju. I zbog toga je korišćenje izvršilaca bolje
od eksplicitnog pravljenja niti (objekata tipa Thread).
Ponekad kod za pravljenje niti treba sakriti u klasi p om oću unutrašnje klase, kao što
ću sada uraditi:

//: paralelno/VarijanteNiti.java
// Pravljenje niti pomoću unutrašnjih klasa.
import java.util.concurrent.*;
import static net.mindview.uti1 .Print.*;

// Upotrebiću imenovanu unutrašnju 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);
}
}

// Upotrebiću anonimnu unutrašnju 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 izvršavanje 909

// Upotrebiću 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);
}

// Upotrebiću 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 izvršavati 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 unutrašnju klasu koja nasleđuje Thread i u kon-
struktoru pravi instancu te unutrašnje klase. To ima smisla ako unutrašnja klasa ima po-
sebne sposobnosti (nove m etode) potrebne u drugim m etodam a. M edutim , nit obično
pravim o sam o zbog sposobnosti klase Thread, pa nije n eophodno da pravim o imenova-
nu unutrašnju klasu. U nutrasnjaN itž pokazuje drugačiji pristup: u konstruktoru se pravi
ano nim na unutrašnja potklasa od Thread i svodi naviše na Thread referencu t. Ukoliko
Poglavlje 2 1: Paralelno izvršavanje 911

ostalim m etodam a te klase zatreba pristup objektu na koji upućuje t, m ogu to učiniti pre-
ko interfejsa T hread i bez poznavanja stvarnog tipa tog objekta.
Treća 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 vraća svoj rezultat nakon što nit otpočne s radom .
Ukoliko nit obavlja sam o pom oćne, 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.
Vežba 10: (4) Izm enite vežbu 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 vraća 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 više
načina i to program era m ože da zbuni. Često problem nastaje zbog term inologije kojom
je opisan program za paralelno izvršavanje, naročito tam o gde su u pitanju niti.
Dosad je trebalo da uvidite kako postoji razlika izm eđu zadatka i niti koja taj zadatak
izvršava; ta razlika je naročito jasna u Javinim bibliotekam a, pošto nad klasom Thread za-
pravo nem am o nikakvu kontrolu (a još je jasnija kada se radi o izvršiocima koji um esto
nas prave niti i upravljaju njim a). Program er piše zađatak i na neki način m u dodeljuje nit
koja će ga izvršavati.
U Javi, nit (klasa Thread) sam a po sebi ne radi ništa. O na izvršava zadatak koji joj da-
mo. Pa ipak, u literaturi o višenitnom program iranju uvek se kaže ,,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 očigledno treba da izve-
dem iz klase Thread. Dodajte tom e loše odabrano ime interfejsa Runnable (koji se može
izvršavati); po m om mišljenju, trebalo je da se zove Task (zadatak). Ukoliko interfejs
očigledno nije ništa više od generičkog kapsuliranja svojih m etoda, onda je im enovanje
po kalupu„m ože da uradi lo i to“ odgovarajuće, ali ako treba da izrazi viši pojam - kao što
je Zadatak - o nda m u treba dati ime tog pojma.
Problem potiče od mešanja nivoa apstrakcije. Na nivou koncepta, želimo da napravi-
m o zadatak koji se izvršava nezavisno od drugih zadataka, pa bi trebalo da je moguće de-
finisati zadatak i reći ,,radi“, bez petljanja s detaljima. Ali fizički, pravljenje niti je skupo, pa
m oram o da ih štedim o i upravljam o njim a. Zato sa stanovišta realizacije im a smisla od-
vojiti zadatke od niti. Sem toga, Javine niti su napravljene na osnovu nekakvih p-niti koje
potiču još iz C-a, a u tom jeziku program er je opkoljen detaljim a i m ora poznavati do u
sitnicu sve što se događa. Delimično je takav pristup prešao i u realizaciju Jave, pa da bi-
sm o ostali na visokom nivou apstrakcije, m oram o biti disciplinovani pri pisanju progra-
ma. (Potrudiću se da u ovom poglavlju sam pokažem tu disciplinu.)
Da bi razm atranje bilo jasnije, term inom zadatak opisivaću ono što se radi, a term i-
nom nit - m ehanizam koji izvršava zadatak. Dakle, ako sistem razm atram o na koncep-
tualnom nivou, m ožem o upotrebiti term in zadatak, a da uopšte ne spom injem o
m ehanizam izvršavanja.
912 Misliti na Javi

Kako se pridružiti postojećoj niti


Jedna nit m ože pozvati m etod u jo in ( ) za neku drug u nit; u to m slučaju, prva n it čeka da
druga završi rad pa tek onda nastavlja izvršavanje. Ukoliko n it pozove n .jo in ( ) za drugu
nit t kao argum ent, pozivajuća nit će biti zaustavljena sve dok odredišna nit t ne završi
s radom - dok t.isAlive( ) ne po p rim i vrednost false.
M etodu jo in ( ) možete pozvati i s vrem enskim argum entom (izraženim u m ilisekun-
dam a ili m ilisekundam a i nanosekundam a); u tom slučaju se izvršavanje m etode jo in ( )
završava ukoliko se odredišna n it u tom vrem enu ne završi.
Pošto poziv m etode jo in ( ) m ože biti prekinut pozivom m etode in te rru p t( ) za pozi-
vajuću nit, obavezna je upotreba odredbe try-catch.
Sve te operacije se izvršavaju u sJedećem prim eru:

//: paralelno/Joining.java
// Objašnjenje 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 probuđena");
}
}

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 izvršavanje 913

print(getName() + čekanje završeno");


}
}

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 završeno
Pospana je probuđena
Omamljena: čekanje završeno
* ///:-

Spavac je nit koja odlazi na spavanje za vrem e specificirano u njenom konstruktoru.


U nutar nietode r u n ( ), m etoda sleep ( ) može završiti s rad o m kada to vrem e istekne, ali
može takode biti i prekinuta u izvršavanju. 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 izvršavanju. M eđutim , 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
može da ispita svoje prekinuto stanje, a u kojima se ne generiše izuzetak.
Joiner je zadatak koji čeka da se Spavac probudi; čekanje se postiže 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 završava svoje izvršavanje zajedno s tim objektom
tipa Spavac, bez obzira na to da li je Spavac bio prekinut ili je norm alno okončao svoje
izvršavanje.
Imajte u vidu da Java SE5 biblioteke java.util.concurrent sadrže alatke. M eđu njim a je
CyclicBarrier (opisaću je u nastavku poglavlja); ona m ože biti podesnija od m etode
jo in ( ) koja je bila deo prvobitne biblioteke za višenitno program iranje.

Korisničko okruženje koje brzo reaguje


Već sam rekao da je jedan od motiva za višenitno program iranje potreba da se dobiju
korisnička okruženja koja brzo reaguju. Iako sve do poglavlja Grafička korisniika
okruženja nećem o imati posla s grafičkim okruženjim a, naredni prim er je jednostavna
skica konzolnog korisničkog okruženja. Prim er im a dve verzije: jednu koja stalno nešto
računa i zato ne stiže da pročita ulaz s konzole, i drugu u kojoj je izračunavanje stavljeno
u zaseban zadatak, pa pored njega program stiže da osluškuje / ulaz s konzole.
914 Misliti na Javi

//: paralelno/BrzoKorisnickoOkruzenje.java
// Brzina reagovanja korisničkog okruženja.
// {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 izvršava
}
}

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 izračunavanje u n u tar beskonačne petlje while, te
očigledno ne može da dopre do reda u kojem se čita ulaz s konzole (uslov ■vvhile čini da se
prevodilac prevari i oceni da je taj red dostižan). Ukoliko red u kojem se pravi novo Spo-
roK o risnicko O kru zenje pretvorite u naredbu koja se izvršava, m oraćete taj proces ručno
da ubijete da biste izašli iz program a.
Program će brzo reagovati ukoliko izračunavanje stavite u n u tar m etode r u n ( ) i tako
om ogućite da ono bude predupređeno. Kada pritisnete taster Enter, videćete da se izraču-
navanje odvija u pozadini dok program osluškuje ulaz korisnika.

Grupe niti
G rupa niti sadrži kolekciju niti. V rednost grupa niti sažeo je Joshua Bioch,8 projektant
softvera koji je (izm edu ostalog), dok je radio za Sun, popravio i znatno poboljšao biblio-
teku Java kolekcija u JDK 1.2:
„G rupe niti je najbolje sm atrati neuspešnim 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 izvršavanje 915

Ukoliko ste (kao ja) potrošili vreme i energiju pokušavajući da shvatite vrednost grupa
niti, m ožda se pitate zašto Sun o toj tem i nikada nije dao zvanično saopštenje. Isto važi i za
brojne druge prom ene sprovedene u Javi tokom godina. Izgleda da bism o mogli prim eniti
Teoriju rastućeg angažovanjd' - njen tvorac je Joseph Stiglitz (dobitnik Nobelove nagrade).
„Cenu nastavljanja grešaka plaćaju drugi, dok cenu priznavanja grešaka plaćam o sami.“

Hvatanje izuzetaka
Izuzetak koji je pobegao iz niti ne m ožete uhvatiti - takva je njihova priroda. Kada izuze-
tak izađe iz m etode r u n ( ) zadatka, prodreće do konzole ukoliko ne preduzm ete posebne
m ere za hvatanje takvih zabludelih grešaka. Pre Jave SE5, za hvatanje takvih izuzetaka ko-
ristili sm o grupe niti, ali sada problem m ožem o rešiti izvršiocima, te o grupam a niti više
ne m oram o ništa da znam o (sem onoliko koliko je po trebno da bism o razum eli stari kod;
pojedinosti o grupam a niti pročitajte u knjizi T h in kin g in Java, 2 n“E dition, koju m ožete
preuzeti sa adrese w w w .M indV iew .net).
Sledeći zadatak uvek baca izuzetak koji izlazi iz njegove m etode r u n ( ), a m a in ( ) po-
kazuje šta se dešava 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)

Ništa ne pom aže 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. Zašto da se o g ran ičim o sam o na Javu? Bio sam k o n su ltan t u
više projekata gde se pokazalo da je ta tv rdnja tačna.
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 NEĆE izvršiti!
System.out.println("Izuzetak je obrađen!");
}
}
} ///= -
Rezultat je isti kao u p rethodnom prim eru: izuzetak koji nije bio uhvaćen.
Problem ćemo rešiti prom enom načina na koji izvršilac (Executor) pravi niti. Java SE5
im a nov interfejs po im enu Thread.UncaughtExceptionHandler; pom oću njega svakoj
niti možete da pridružite njen program za obradu. Neposredno pre nego što bi nit um rla
zato što njen izuzetak nije bio uhvaćen, autom atski se poziva Thread.Uncaught-
ExceptionHandIer.uncaughtException( ). Da bism o mogli da ga upotrebimo, napraviće-
m o nov tip interfejsa ThreadFactory koji svakoj niti koju napravi pridružuje Thread.Un-
caughtExceptionHandler. Tu proizvodnu m etodu prosledićemo 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("izvršava 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 izvršavanje 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
izvršava 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. Možete videti da neuhvaćene izuzetke
sada hvata m etoda neuhvacenlzuzetak( ).
G ornji prim er om ogućuje da blok za obradu (engl. handler) prilagođavate svakom po-
jedinom slučaju. 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 neuhvaćenih izuzetaka koji
postavlja odredeno statičko 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 slučaju da ne postoji blok za obradu neuhvaćenih izu-
zetaka prilagoden svakoj niti. Sistem proverava postoji li verzija za konkretnu nit i ako je
ne pronađe, 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 ožete da zamislite kao usam ljenu jedinku koja se kreće kroz pro-
stor vašeg problem a i rešava jedan po jedan zadatak. Pošto postoji samo jedna jedinka,
uopšte ne m orate da vodite računa o situaciji kada dve jedinke pokušavaju da iskoriste isti
resurs istovremeno, p o p u t dvoje koji pokušavaju da se parkiraju na istom mestu, ili da
prođu kroz vrata istovrem eno ili čak da sam o govore istovremeno.
Ako za rešavanje problem a upotrebite više niti, ne javlja se problem usam ljenih jedin-
ki, ali postoji m ogućnost da dve ili više niti pokušaju da upotrebe isti ograničen resurs u
istom trenutku, tj. da se uzajam no om etu. Sukobljavanje zbog nekog resursa m ora se
sprečiti ili će dve niti istovrem eno pokušati da pristupe istom bankovnom nalogu,
odštam paju nešto na istom štam paču, okrenu isti ventil itd.

Nepravilno pristupanje resursima


Razm otrite sledeći prim er u kojem jedan zadatak generiše parne brojeve, a drugi zadaci ih
troše. Jedini posao zadataka potrošača jeste da provere validnost parnih brojeva.
Prvo ćem o definisati potrošački zadatak ProveraParnosti, pošto ć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, napravićem o apstraktnu klasu GeneratorCe-
lobroj koja sadrži 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 generičke 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();
// Dopustiću 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 utvrđuje da li je pravljenje objekta otkazano. Pošto je
indikator cancelled tipa boolean, on je a to m ski (nedeljiv), što znači đa takvo polje ne
m ožete pročitati u nekom m eđustanju usred jednostavnih operacija kao što su dodelji-
vanje i vraćanje 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
saznaćete više u nastavku poglavlja.
Sledeća klasa ProveraParnosti može da testira svaki GeneratorCelobroj:

//: paralelno/ProveraParnosti.java
import java.util.concurrent.*;
Poglavlje 2 1: Paralelno izvršavanje 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 izađete
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 pažnju na to da, u ovom prim eru, klasa čije se pravljenje može 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 način, zadaci koji dele zajednički resurs (GeneratorCelobroj) oslušku-
ju signal tog resursa koji im kazuje kada da okončaju svoj rad. Time se izbegava tzv. uslov
za trku (engl. race condition ), gde se dva ili više zadataka trkaju da reaguju na ispunjenje
nekog uslova i zato se sudare ili na drugi način proizvedu nedosleđne rezultate. Pažljivo
razmislite o svim m ogučim načinim a na koje paralelni program može da zakaže; m orate
napraviti zaštitu od svakog. Na primer, zadatak ne sm e da zavisi od drugog zadatka, zato
što Java ne jem či redosled gašenja 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
pokreće više ProveraParnosti za isti GeneratorCelobroj. Ukoliko GeneratorCelobroj
nije dao željeni rezultat, m etoda te s t( ) će to prijaviti i izaći; u protivnom , m orate
pritisnuti kom binaciju tastera C ontrol-C da biste je okončali.
920 Misliti na Javi

Zadaci ProvereParnosti stalno čitaju i ispituju brojeve koje je dao njim a pridruženi
GeneratorCelobroj. Ukoliko generator.isC ancelled( ) im a vrednost true, m etoda r u n ( )
vraća svoj rezultat i izlazi, što izvršiocu u m etodi ProveraParnosti.test( ) kazuje da je
zadatak obavljen. Svaki zadatak ProvereParnosti m ože da pozove m etodu cancel( ) za
svoj pridruženi G eneratorCelobroj, čim e prouzrokuje da sve druge ProvereParnosti tog
objekta tipa GeneratorCelobroj n orm alno okončaju svoj rad. U nastavku poglavlja vi-
dećete da Java im a i opštije m ehanizm e za okončavanje 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 izađete iz programa
89476993 nije paran!
89476993 nije paran!
* ///:-

Može se desiti da jedan zadatak pozove n e x t ( ) nakon što je drugi zadatak obavio prvo
povećanje 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 ože 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 pronađe neki koji nije paran, prijavljuje se greška i program se gasi.
Taj program pre ili kasnije m ora da otkaže, 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 eđutim ,
problem ne m ora biti otkriven dok G en e ra to rP a rn ih ne izvrši 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 eđu prvog i drugog povećanja za 1 stavite poziv m etode y ie ld ( ). I to
je deo problem a s višenitnim program im a - um eju da daju privid ispravnosti čak i kada
greška postoji, ukoliko je verovatnoća otkazivanja veoma mala.
Važno je uočiti da se operacija povećanja za 1 interno sastoji od više koraka, i da više-
nitni m ehanizam može zaustaviti (prekinuti) zadatak usred tih koraka - drugim rečima,
u Javi povećanje za 1 nije atom ska operacija. Dakle, čak se ni povećanje za 1 ne može oba-
viti bezbedno ako zadatak nije zaštićen.
Poglavlje 21: Paralelno izvršavanje 921

Razrešavanje takmičenja 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 viljuškom u ruci i da upravo nam eravate da na-
bodete poslednje parče hrane u tanjiru; u trenu tk u kad viljuška dodirne to parče, ono iz-
nenada nestane (pošto je vaša nit prekinuta, a pokrenuta druga n it koja je uletela i ukrala
to parče). Sada razum ete o kakvom problem u se radi. Da bi paraleino izvršavanje funk-
cionisalo, barem tokom bitnih razdoblja m orate sprečiti da više zadataka istovrem eno
pristupi istom resursu.
Takvi sudari jednostavno se sprečavaju zaključavanjem (engl. lock ) resursa d o k g a jed-
na nit koristi. Prva nit koja pristupi nekom resursu p rethod n o ga zaključa, čim e se spreča-
va da m u druge niti pristupaju sve dok ne b ude otključan; tada ga druga nit zaključava,
koristi ga itd. Ako prednje sedište u kolim a predstavlia ograničen resurs, dete koje poviče:
,,Prvi!“ i sedne na njega, zaključava ga.
Da bi se rešio problem sudaranja niti, gotovo sve šeme paralelnog rada serijalizuju p ri-
stup deljenim resursima. To znači da je, u svakom trenutku, p ristup deljenom resursu doz-
voljen sam o jednom zadatku. To se obično postiže stavljanjem koda u o dredbu koja, u
svakom trenutku, dozvoljava sam o po jednom zađatku d ap ro lazi k ro ztaj deo koda. Pošto
ta odredba proizvodi uzajam no isključivanje (engl. m u tu a l exclusioti), takav m ehanizam
se obično naziva uzajam no isključiva brava (engl. m u tex).
Uzmimo kao prim er vaše kupatilo; više osoba (zadataka koji se izvršavaju u nitim a)
može poželeti da sam ostalno koristi kupatilo (deljeni resurs). Kada pođe u kupatilo, oso-
ba kuca na vrata kako bi utvrdila da li je slobodno. Ako jeste, osoba ude i zaključa vrata.
Nadalje je svima drugim a koji požele da koriste to kupatilo korišćenje ,,blokirano“, pa
m oraju da čekaju ispred vrata dok se kupatilo ne oslobodi.
Analogija donekle popušta kada se kupatilo oslobodi i dođe vreme da se prepusti d ru -
gom zadatku na korišćenje. Nema reda za čekanje i mi ne m ožem o znati ko će sledeći do-
biti resurs na korišćenje, zato što m ehanizam za raspodelu procesorskog vrem ena nitim a
nije u tom smislu determ inistički. Za razliku od toga, kao da grupa blokiranih zadataka
kruži ispred kupatiia, i kada zadatak koji ga je zaključao otključa i izade, ulazi onaj koji se
u tom tren utku zatekao najbliže vratim a. Već sam rekao da m ehanizm u za raspodelu pro-
cesorskog vrem ena nitim a pom oću m etoda y ie ld ( ) i se tP rio rity ( ) predlažete kom e da
dodeli resurs, ali ti predlozi ne m oraju im ati m nogo uticaja, pošto to zavisi od platform e
i realizacije JVM-a.
Da bi se sprečili sukobi zbog resursa, Java im a ugradenu podršku u vidu rezervisane
reči synchronized. Kada zadatak treba da izvrši deo koda zaštićen rezervisanom rečju
synchronized, on najpre utvrdi da je resurs otključan, zaključa ga, izvrši kod i onda
otključa.
Deljeni resurs je najčešće deo m em orije u obliku objekta, ali m ože biti i datoteka, U/I
prikljućak 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 izvršava 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 naučili da podaci klasa m oraju biti privatni (im ati m odifikator private) u kodu
koji se isporučuje kupcima; toj m em oriji treba da pristupate sam o preko m etoda. Sukobe
ćete sprečiti 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 zaključava i nijedna druga sinhronizo-
vana m etoda tog objekta ne m ože biti pozvana dok se prva ne završi i ne otključa tu bravu.
Za prethodne m etode važi sledeće: 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 završi rad i ne otključa
bravu. Dakle, postoji samo jedna brava koju dele sve sinhronizovane m etode određenog
objekta, i ona sprečava da više zadataka istovremeno upisuje u m em oriju objekta.
Vodite računa o tom e da je za paralelno izvršavanje izuzetno važno da polja b u d u pri-
vatna; ako nisu, rezervisana reč synchronized ne m ože sprečiti druge zadatke da polju
pristupaju neposredno i tako izazivaju sukobe.
Isti zadatak može više puta zaključati objekat. To se dešava kada jedna m etoda za isti
objekat pozove drugu m etodu, a druga za isti objekat pozove treću itd. JVM prati broj
zaključavanja objekta. Broj zaključavanja otključanog objekta je nula. Kada zadatak prvi
put zaključa taj objekat, broj zaključavanja se poveća za jedan. Svaki p ut kada taj zadatak
ponovo zaključa taj objekat, broj zaključavanja se poveća za jedan. Naravno, samo
zadatak koji je prvi zaključao taj objekat m ože da ga zaključa više puta. Broj zaključavanja
se sm anjuje za jedan kad god taj zadatak izađe iz neke od sinhronizovanih m etoda; kada
broj zaključavanja 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 statičke m etode (označene kao synchronized i static) mogu jed-
na drugu sprečiti da istovrem eno pristupe statičkim podacim a u okviru pojedine klase.
Kada sinhronizovati? Prim enite B rajanovopravilo sinhronizacijc:'0
Sinhronizaciju m orate upotrebiti ukoliko pišete u prom enljivu koju bi zatim mogla da
pročita 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 oću iste brave (m onitora).
Ako u klasi im ate više 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 važno: sve m etode koje pri-
stupaju kritičnom resursu m oraju biti sinhronizovane ili program neće raditi kako treba.

Sinhronizovanje GeneratoraParnih
Ako klasi G eneratorParnih.java dodam o rezervisanu reč synchronized, sprečićemo
neželjeno 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 izvršavanje 923

//: paralelno/SinhronizovanGeneratorParnih.java
// Pojednostavljivanje uzajamno isključivih brava
// pomoću rezervisane reči synchronized.
// {RunByHand}

public class
SinhronizovanGeneratorParnih extends GeneratorCelobroj {
private int tekuciParniBroj = 0;
public synchronized int next() {
++tekuciParniBroj;
Thread.yield(); // Brže 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 između dva povećanja za 1 da bi se povećala
verovatnoća prebacivanja konteksta dok je tekuciParniBroj neparan. Pošto uzajam no
isključiva brava sprečava da više zadataka u isto vreme bude u kritičnom delu program a,
do otkazivanja neće doći, ali poziv m etode y ie ld ( ) pom aže da se otkazivanje ranije desi
ukoliko već može da nastane.
M etodu n e x t( ) zaključava prvi zadatalc koji uđe u nju, i svi drugi zadaci koji pokušaju
da je zaključaju bivaju blokirani sve dok je prvi zadatak ne otključa. Tada m ehanizam za
raspodelu procesorskog vrem ena odabire jedan od ostalih zadataka koji čekaju da se bra-
va otključa. Na taj način, u svakom trenutku samo po jedan zadatak može proći kroz kod
koji čuva uzajam no isključiva brava (mutex).
Vežba 11: (3) N apravite klasu koja sađrži dva polja podataka i m etodu koja radi s tim
poljim a u postupku koji traje više koraka, tako da tokom izvršavanja 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 više niti za pozivanje raznih m etoda i pokažite da su poda-
ci vidljivi i kada su nepravilni. Rešite problem pom oću rezervisane reči synchronized.

Upotreba eksplicitnih brava (Lock objekata)


Biblioteka Java SE5 java.util.concurrent sađrži i eksplicitan m ehanizam uzajam no
isključive brave (mutexa) definisan u paketu java.util.concurrent.locks. O bjekat tipa
Lock m orate eksplicitno praviti, zaključavati i otključavati; zato je njegov kod m anje ele-
gantan od ugradenih oblika. M eđutim , prilagodljiviji je za rešavanje odredenih vrsta pro-
blema. Evo kako izgleda SinhronizovanGeneratorParnih.java u verziji sa eksplicitnim
bravam a (objektim a tipa Lock):

//: paralelno/MutexGeneratorParnih.java
// Sprećavanje sukoba niti pomoću uzajamno isključivih 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(); // Brže 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
kritični 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 način da brava uvek bude otključa-
na. Naredba return mora biti u n u ta r odredbe try da otključavanje m etodoin u n lock( ) ne
bi došlo prerano i izložilo podatke d ru gim zadacima.
lako blok try-finally zahteva više koda nego rezervisana reč synchronized, on pred-
stavlja i jednu od prednosti eksplicitnih Lock objekata. Kada program otkaže uz rezervi-
sanu reč synchronized, baca se izuzetak, ali program er nem a priliku da počisti i tako
sistem vrati u dobro stanje. Ukoliko upotrebljavam o eksplicitne Lock objekte, im am o
odredbu finally unutar koje m ožem o obaviti sve čišćenje potrebno da bism o sistem vra-
tili u odgovarajuće stanje.
Uopšte uzev, uz synchronized se piše m anje koda, pa je m anja prilika da program er
pogreši. Stoga se eksplicitni Lock objekti obično koriste sam o za rešavanje specijalnih
problem a. Na primer, uz rezervisanu reč synchronized svakako ćete uspeti da zaključate
zadatak. Uz to, ne možete pokušavati da zaključate zadatak pa odustati - m orate
upotrebiti biblioteku concurrent:

//: paralelno/PokusajZakljucavanja.java
// Brave u biblioteci concurrent dozvoljavaju
// da odustanete od pokušaja zaključavanja.
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 izvršavanje 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 otključana
pz.odredjenoVreme(); // True — brava je otključana
// Sada napravi zaseban zadatak koji će pokušati da zaključa:
new Thread() {
{ setDaemon(true); }
public void run() {
p z .brava.1ock();
System.out.pri ntln("zaključano");
}
}.start();
Thread.yield(); // Daj priliku drugom zadatku
pz.neodredjenoVremef); // False -- prethodni zadatak je več zaključao
pz.odredjenoVremeO; // False — prethodni zadatak je već zaključao
}
} /* Ispis:
tryLock(): true
tryLock(2, TimeUnit.SECONDS): true
zaključano
tryLock(): false
tryLock(2, TimeUnit.SECONDS): false
* ///:-

O bjekat tipa ReentrantLock om ogućuje da pokušate zaključati bravu i da u tom e ne


uspete; ako je neki drugi zadatak bravu već zaključao, možete nešto 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

Pokušaj zaključavanja u m etodi odredjenoV rem e( ) traje najviše 2 sekunde dok se ne


proglasi neuspešnim (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 zaključava bravu, da bi
m etode neodredjenoV rem e( ) i odredjenoV rem e( ) im ale oko čega da se takmiče.
Eksplicitni objekti tipa Lock om ogućuju finiju kontrolu nad zaključavanjem i otključa-
vanjem od ugrađenih brava synchronized. O ni služe za realizađju specijalizovanih struk-
tu ra za sinhronizaciju, kao što je povezano zaključavanje (engl. hand-over-hand locking ili
lock coupling) koje se upotrebljava za prolaz kroz ulanćanu listu - kod za prolaz m ora
zaključati bravu sledećeg čvora pre nego što otključa bravu tekućeg čvora.

Atomske operacije i trenutna vidljivost


U raspravam a o višenitnom program iranju na Javi često se ponavlja netačna priča 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 ože da prekine; ako takva
operacija otpočne, prebacivanje konteksta će postati m oguće tek kada se ona završi.
O sloniti se na neprekidnost operacije je škakljivo i opasno —ne bi trebalo da se usudite da
um esto sinhronizacije koristite neprekidnost ukoliko niste stručnjak za paralelno pro-
gram iranje ili ukoliko vam takav stručnjak ne pom aže. Ako mislite da ste dovoljno pa-
m etni da se igrate s vatrom , podvrgnite se ovom testu:
Gecov test1': Ako umete da napišete JVM visokih perform ansi za savrem eni m ikropro-
cesor, onda ste kvalifikovani da razm islite o tom e m ožete 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 pročitajte
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 zajemčeno su neprekidne (atomske) operacije. M eđutim , čitanje i pisanje 64-bitnih
vrednosti (promenljivih tipa long ili double) JVM sme da podeli na dve zasebne 32-bitne
operacije; tim e se povećava verovatnoća da prebacivanje konteksta nastane usred čitanja ili
pisanja, pa bi razni zadaci videli pogrešne rezultate (to se ponekad naziva cepanje reči, pošto
se može desiti da vidite vrednost, tj. rezultat operacije, nakon što je izračunat samo jedan
njen deo). Neprekidnost (jednostavnih dodeljivanja i vraćanja 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 jemče i nešto više, 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 može da prekine
atom ske operacije. Izuzetno stručni program eri to koriste za pisanje kodn hez zaključa-
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 napišem ovo poglavlje na osnovu njegovih sam o đelim ičn 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 važne odluke o projektu na kojem radite. Ukoliko to nikako ne
m ožete da izbegnete, im ate problem .“
Poglavlje 21: Paralelno izvršavanje 927

Katkada izgleda da bi atom ska operacija trebalo da bude bezbedna, ali ona to nije. Većina
ćitalaca ove knjige sigurno neće m oći da položi spom enuti Gecov test, te uopšte nisu kva-
lifikovani n i da pokušaju da sinhronizaciju zam ene atom skim operacijama. Pokušaj
uklanjanja sinhronizacije obično je znak prerane optim izacije i prouzrokuje grdne pro-
bleme, a dobitak je mali ili nikakav.
Na višeprocesorskim sistemima (koji se sada prave u obliku procesora s više jezgara
- to je više procesora u jednom integrisanom kolu), m nogo veći 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 smešterte u lokalni keš procesora, na pri-
m er), pa razni zadaci dobijaju različite uvide u stanje aplikacije. S druge strane,
m ehanizam sinhronizacije obezbeđuje da izm ene koje na višeprocesorskom sistemu na-
pravi jedan zadatak b u du vidljive celoj aplikaciji. Bez sinhronizacije se ne m ože znati kada
će izm ene postati vidljive.
Vidljivost u celoj aplikaciji obezbeduje i rezervisana reč volatile. Ako deklarišete da je
neko polje volatile, to znači da će svi zadaci koji ga čitaju videti izmenu čim se upisivanje
u njega završi. To važi čak i za lokalnu keš m em oriju, zato što se polja označena 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 različiti 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 više 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 zaštićeno
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 funkcioniše kada vrednost polja zavisi od njegove prethodne vrednosti
(takvo je povećanje vrednosti brojača za 1), niti funkcioniše za vrednosti koje su ogra-
ničene vrednostim a drugih polja, kao što su lovver (donja) i upper (gornja) granica ob-
jekta tipa Range koje m oraju da zadovolje ograničenje lovver <= upper.
U potreba m odifikatora volatile umesto rezervisane reči synchronized bezbedna je naj-
češće jedino u slučaju da klasa ima samo jedno prom enljivo polje. Ponavljam, najbolje je
izabrati rezervisanu reč synchronized - to je najbezbedniji pristup, a sve ostalo je rizično.
Koje operacije su atomske? Dodeljivanje i vraćanje vrednosti polja obično su atomske
operacije. M eđutim , u C++-U bi atom ske mogle biti čak i ove operacije:

i++; // Možda je atomska u C++-U


i += 2; // Možda je atomska u C++-U

Ali u C++-U to zavisi od prevodioca i procesora (zato piše m ožda). Na C++-U se ne


može pisati prenosivi (m eđuplatform ski) kod koji koristi neprekidnost operacija, pošto
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 sledećem 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 sledeće 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 između kojih ima drugih instrukcija.
Dakle, izm eđu čitanja (get) i upisivanja (put) neki drugi zadatak može 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 sledećem 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 izvršavanje 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 eđutim , program je pronašao neparan broj i prekinuo rad. Prem da je retu rn i zaista
atom ska operacija, zbog nepostojanja sinhronizacije i može biti pročitan dok je objekat u
nestabilnom m eđustanju (izm eđu jednog i drugog i++). Sem toga, biće i problem a s vid-
ljivošću, pošto ispred i nem a m odifikatora volatile. O be m etode, i dajV rednost( )
i povecanjeZaD va( ) m oraju biti sinhronizovane. Samo su stručnjaci za paralelno pro-
gram iranje kvalifikovani da u ovakvim situacijam a pokušaju optim izaciju; ponavljam ,
trebalo bi da prim enjujete Brajanovo pravilo za sinhronizaciju.
Kao drugi prim er, razm otrite nešto 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 višenitno izvršavanje
}
} III--
GeneratorSerijskihBrojeva je otprilike najjednostavnija klasa koja se m ože zamisliti,
i ako ste dosad koristili C++ ili neki drugi jezik u kojem se program ira na niskom nivou,
m ožda očekujete da je povećanje za 1 (engl. increm ent ) atom ska operacija, pošto se ope-
racija povećanja za 1 u C + + -u često može realizovati kao jedna m ikroprocesorska in-
strukcija (iako ni to ne na način pouzdan na svim platform am a). M eđutim , već sam rekao
da u Javi povećanje 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 može da ima lokalni stek
i da u njem u održava 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 održavaju u sinhronizaciji s lokalnim podacim a u
nitim a. Zato se čitanja i upisivanja obavljaju neposredno u glavnoj m emoriji, a ne u kešu.
M odifikator volatile ograničava i prevodiočevo preraspoređivanje pristupa tokom optim i-
zacije. M eđutim , volatile ne utiče na činjenicu da povećanje za 1 nije atomska operacija.
U suštini, definišite da je polje volatile ukoliko m u više zadataka može 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 keširan 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 neće ponestati me-
m orije ni u slučaju da pronalaženje problem a potraje. Ovde prikazani KruzniSkup po -
novo upotrebljava m em oriju u kojoj su bili uskladišteni celi brojevi, uz pretpostavku da
će verovatnoća sukoba sa zam enjenim brojevim a biti m inim alna kada se s kraja skupa
vratim o na njegov početak. M etode a d d ( ) i co n tain s( ) sinhronizovane su da bi se spreći-
Io sudaranje niti:

//: paralelno/ProveraSerijskihBrojeva.java
// Operacije prestaju da budu bezbedne
// kada izvršavanje postane višenitno.
// {Args: 4}
import java.uti1 .concurrent.*;

// Ponovo upotrebljava isto parče 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 početak niza i počni da prepisuješ
// nove elemente preko starih:
indeks = ++indeks % duzina;
Poglavlje 2 1: Paralelno izvršavanje 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 pronađen");
System.exit(0);
}
}
} /* Ispis: (primer)
Duplikat: 8468656
* ///:-

ProveraSerijskihBrojeva sadrži statički KruzniSkup koji obuhvata sve proi/vedene


serijske brojeve i ugnežđenu klasu ProveraSerijskih koja proverava da li je svaki od tih se-
rijskih brojeva jedinstven. Kada napravite više zadataka koji se takm iče za dobijanje se-
rijskih brojeva, videćete da će jedan od njih pre ili kasnije dobiti postojeći serijski broj
(duplikat), sam o ga dovoljno dugo pustite da radi. Problem ćete rešiti sinhronizovanjem
m etode sledeciSerijskiBroj( ). (Dodajte rezervisanu reč synchronized ispred nje.)
Ako išta, onda bi čitanje i dodeljivanje prostih tipova trebalo da b u d u atom ske opera-
cije. M eđutim , 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 eđustanju.
D onositi bilo kakve zaključke o tom e škakljivo je i opasno. N ajrazboritije je prosto pri-
državati se Brajanovog pravila za sinhronizaciju.
Vežba 12: (3) Popravite TestNeprekidnosti.java pom oću rezervisane reči synchronized.
U m ete li da pokažete kako je program sada ispravan?
Vežba 13: (1) Popravite program ProveraSerijskihBrojeva.java p o m o ću rezervisane reči
synchronized. U mete li da pokažete 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 obezbeđuju atom sko uslovno ažuriranje oblika:

boolean uporediIAžuriraj(očekivanaVrednost, novaVrednost);

O ne su nam enjene za fino podešavanje p ri kom e se koriste atom ske operacije (nepie-
Iđdnosti) dostupne na nekim od savrem enih procesora, pa o njim a po pravilu ne bi tre-
balo da razbijate glavu. Ponekad m ogu da posluže i za obično program iranje, ali opet
tam o gde se radi o podešavanju perform ansi. Na prim er, izm enićem 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); // Završi 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 izvršavanje 933

System.out.println(vre);
System.exit(0);
}
)
}
} ///= -
U potrebom klase Atom iclnteger izbegli sm o korišćenje rezervisane reči synchroni-
zed. Pošto 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 običnim 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 korišćenjem klase Atoiniclnteger izbegli sve ostale oblike sinhronizacije.


Treba naglasiti da klase iz paketa Atomic služe 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 možete da izbegnete sve druge m oguće problem e.
O bično je bezbednije rađiti s bravam a i zaključavanjem (pom oću rezervisane reči syn-
chronized ili eksplicitnih objekata tipa Lock).
Vežba 14: (4) Pokažite da java.util.Tim er daje velike brojeve tako što ćete napisati pro-
gram koji generiše m noge Tim er objekte što obavljaju neke jednostavne zadatke kad iste-
kne vreme.

Kritični 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 kritičan (engl. critical), a rezervisana reč synchronized se u kri-
tičnom delu program a koristi drugačije. U sledećem prim eru reč synchronized zaključa-
va bravu određenog objekta da bi sinhronizovala obuhvaćeni blok koda:

synchronized(brava) {
// Ovom kodu može da pristupi samo jedna nit u jednom trenutku
93 4 Misliti na Javi

O buhvaćeni blok koda naziva se sinhrortizovani blok, pre ulaska u sinhronizovani blok,
raorate da zaključate objekat brava. Ako je neka druga nit već zaključala taj objekat, u taj
blok nije moguće ući sve dok ne bude otključan.
U sledećem prim eru upoređena su oba p ristupa sinhronizađji. Pokazano je da se vre-
m e tokom kojega je objekat dostupan d rugim zadacim a znatno produžava kada se koristi
sinhronizovani blok um esto sinhronizovanja cele m etode. Pored toga, pokazano je kako
se nezaštićena klasa m ože upotrebiti u višenitnoj situaciji ukoliko je kontroliše i štiti dru-
ga klasa:

//: paralelno/KriticanDeo.java
// Sinhronizovanje blokova umesto celih metoda. Pokazuje i zaštitu klase
// nebezbedne u višenitnom izvršavanju pomoću druge koja je bezbedna.
package concurrency;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util

class Par { // Nije bezbedna u višenitnom izvršavanju


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

// Zaštita Para unutar klase bezbedne u višenitnom izvršavanju:


abstract class MenadzerPara {
Poglavlje 21: Paralelno izvršavanje 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() {
// Napraviću kopiju da bi original bio bezbedan:
return new Par(p.dajX(), p .dajY());
}
// Pretpostaviću da je ova operacija dugačka
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 ) ;
}

// Zaštitiću samo kritičan 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 {


// Poređenje dva različita 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 izvršavanje 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 višenitnom izvršavanju zato što njegova invarijan-
ta (priznajem , sasvim proizvoljno) zahteva da obe prom enljive zadrže istu vrednost. Sem
toga, već ste u p retho dn om delu poglavlja videli da operacije povećanja za jedan nisu bez-
bedne u višenitnom izvršavanju, a pošto nijedna od m etoda nije sinhronizovana, ne
m ožete očekivati da će objekat tipa Par ostati nepokvaren u višenitnom program u.
Recimo da vam je neko dao klasu Par koja nije bezbedna u višenitnom izvršavanju, a vi
m orate da je upotrebite u višenitnom okruženju. To ćete postići pravljenjem kiase Mena-
dzerPara koja sadrži objekat tipa Par i kontroliše pristupanje njemu. Vodite računa 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( ) biće sinhronizovana na m estu svoje realizacije.
S truktura klase MenadzerPara, u kojoj funkcije realizovane u osnovnoj klasi
upotrebljavaju jednu ili više apstraktnih m etoda definisanih u izvedenim klasama, naziva se
projektni obrazac Template M ethod.'5 Projektni obrasci om ogućuju 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 oću
sinhronizovanog bloka sinhronizovan samo deo metode povecajZaJedan( ). O bratite
pažnju na to da rezervisana reč synchronized ne spada u potpis m etode i stoga se m ože do-
dati prilikom preklapanja.
M etoda sacuvaj( ) dodaje olijekat tipa Par sinhronizovanoj listi tipa ArrayList, pa je ta
operacija bezbedna u višenitnom izvršavanju. 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 različite realizacije apstraktne klase MenadzerPara, dok se ProveraPara obavlja
iz drugog zadatka. Svaki put kada je ProveraPara uspešna, ona brojacProvera poveća za 1
i tako prati učestalost 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 izvrša-
vanja, videćete da m etoda M enadzerPara).povecajZaJedan( ) nc dozvoljava klasi Prove-
raPara pristup ni približno onoliko često kao m etoda M enadzerPara2.povecajZaJedan( )
koja ima sinhronizovan blok i zato duže radi bez zaključavanja. O bično je baš to razlog za
upotrebu sinhronizovanog bloka umesto sinhronizacije cele metode: om ogućiti drugim
zadacim a češći i duži pristup (koliko je to bezbedno).
Za zaštitu kritičnih delova program a možete upotrebiti i eksplicitne objekte tipa Lock:

//: paralelno/EksplicitanKriticanDeo.java
// Zaštita kritičnih 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. (Ađdison-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
}

// Kritičan 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 najveći deo program a KriticanDeo.java i napravili nove tipove


od M enadzerPara koji upotrebljavaju eksplicitne objekte tipa Lock. Eksplicitan-
MenadzerPara2 prikazuje pravljenje kritičnog dela pom oću objekta tipa Lock; poziv me-
tode sacuvaj( ) nalazi se izvan kritičnog dela.
Poglavlje 21; Paralelno izvršavanje 939

Sinhronizacija s drugim objektima


S inhronizovanom bloku m ora biti dat objekat s kojim će se sinhronizovati, i obično je za
to najpam etnije upotrebiti tekući objekat za koji se m etoda poziva: synchronized(this);
upravo to je u radeno u klasi MenadzerPara2. Na taj način, ostale sinhronizovane m etode
i kritični delovi objekta ne m ogu biti pozvani nakon zaključavanja sinhronizovanog blo-
ka. Dakle, uticaj sinhronizovanog bloka na kritičan 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 slučaju
svi relevantni zadaci m oraju sinhronizovati sa istim objektom . U narednom p rim eru po-
kazano je da dva zadatka m ogu ući u objekat čije su m etode sinhronizovane s različitim
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 izvršavaju istovrem eno, dakle sinhroniza-
cija jedne ne blokira onu drugu.
Vežba 15: (1) Napravite klasu s tri m etode koje sadrže kritične delove, a sve su
sinhronizovane sa istim objektom . N apravite više zadataka kako biste pokazali da se u
svakom trenutku može izvršavati sam o jedan od njih. Potom m odifikujte m etode tako
da se sinhronizuju s različitim objektim a i pokažite da se sve tri m etode m ogu izvršavati
istovremeno.
Vežba 16: (1) Izmenite vežbu 15 tako da se upotrebljavaju eksplicitni Lock objekti.

Lokalno skladište niti


D rugi način da sprečite sudare zadataka zbog deljenih resursa jeste da izbegnete deljenje
prom enljivih. Lokalno skladište n iti je m ehanizam koji za svaku nit koja pristupa objektu
autom atski pravi novo skladište za istu prom enljivu. Dakle, ako pet niti pristupa objektu
koji im a prom enljivu x, lokalno skladište niti generiše pet različitih skladišta za x. U sušti-
ni, ta skladišta om ogučuju da svakoj niti pridružite njeno stanje.
Pravljenjem i upravljanjem lokalnim skladištem 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 skladište.
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 izvršavanje 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(); // Završiće 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 obično se skladište kao statička polja. Kada napravite objekat
tipa ThreadLocal, sadržaju tog objekta možete pristupiti sam o m etodam a g e t( ) i s e t( ).
Metoda g e t( ) vraća kopiju objekta pridruženu toj niti, dok s e t( ) umeće svoj argum ent u
objekat uskladišten za tu nit i vraća stari objekat koji je bio u tom skladištu. To pokazuju
m etode povecajZaJeđan( ) i g e t( ) program a ThreadLocalSpremnikPromenljivih.
O bratite pažnju na to da m etode povecajZaJedan( ) i g e t( ) nisu sinhronizovane, zato što
ThreadLocal jemči da neće doći do uslova za trku.
Kada pokrenete ovaj program , videćete dokaz da se svakoj niti dodeljuje njeno sop-
stveno skladište, pošto svaka od njih održava sopstveni brojač iako postoji sam o jedan ob-
jekat ThreadLocalSpremnikPromenljivih.
942 Misliti na Javi

Gašenje zadataka
U nekim a od prethodnih prim era, m etode cancel( ) i isCanceIled( ) sm eštene 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 eđutim , u nekim situacijam a zadatak
m ora biti ugašen naglije. U ovom odeljku saznaćete sve o takvom gašenju i problem im a
zbog njega.
Prvo, razm otrim o prim er koji ne sam o da pokazuje problem gašenja, nego je i doda-
tan prim er deljenja resursa.

Ukrasna bašta
U ovoj simulaciji, baštenska komisija bi htela da zna koliko Ijudi svakog dana uđe u baštu
kroz neku od njenih kapija. Svaka kapija im a blokirajuću vrtešku ili neku dru g u vrstu
brojaća, i nakon povećanja brojača blokirajuće vrteške za 1, povećava se i zajednički bro-
jač koji predstavlja ukupan broj posetilaca bašte.

//: 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 izvršavanje 943

// Drži ovaj zadatak u listi. Takođe sprečava


// da mrtvi zadaci budu pokupljeni kao smeće:
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 ugašeni!");
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 bašte i ćuva se kao statičko 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 približno pola vremena, iz-
m eđu upisivanja broja u p rivr i povećanja za 1 i upisivanja promenljive privr nazad u
broj. Ako rezervisanu reč synchronized izbacite iz definicije m etode povecajZaJedan( ),
program otkazuje zato što više 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 ušlo kroz
taj ulaz. Time se može kontrolisati da li objekat broj parnti tačan broj posetilaca.
U laz.ru n ( ) povećava broj i objekat broj i zatim spava 100 milisekundi.
Pošto 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 ožem o se izvući a da ga ne sinhronizujem o. Ukoliko ste iole nesigurni
u takvoj situaciji, bolje upotrebite synchronized.
Ovaj program se prilično m nogo trud i da sve pogasi na stabilan način. Tako sam ga
napisao da bih vam pokazao koliko pažljivo m orate gasiti višenitni program i to koliko je
vredna m etoda in te rru p t( ) o kojoj će uskoro biti više reči.
Nakon 3 sekunde, m a in ( ) šalje statičku poruku cancel( ) na Ulaz, zatim poziva
sh u td o w n ( ) za objekat exec (izvršilac), a onda poziva m etodu aw aitTerm ination( ) za taj
exec. ExecutorService.aw aitTerm ination( ) čeka da se svi zadaci završe i vraća true ako
se svi završe pre isteka zadatog vrem ena čekanja; u protivnom vraća false, što pokazuje da
svi zadaci nisu završeni. Iako ovo prouzrokuje da svi zadaci izađu iz svojih m etoda r u n ( )
Poglavlje 21: Paralelno izvršavanje 945

i stoga završe svoje izvršavanje, objekti tipa Ulaz i dalje su validni zato što je u konstruk-
to ru svaki objekat Ulaz uskladišten u statičkoj listi ulazi tipa List<Ulaz>. Zato
saberiU laze( ) i dalje radi s validnim objektim a Ulaz.
D ok ovaj program bude radio, prikazivaće ukupan broj posetilaca i broj posetilaca na
svakom ulazu koji p ro đ u kroz blokirajuću vrtešku. Ako uklonite deklaraciju synchroni-
zed ispred m etode Broj.povecajZaJedan( ), videćete da ukupan broj posetilaca nije jed-
nak očekivanom . Broj posetilaca koji se prebroji na svim blokirajućim vrteškam a neće
biti jednak iznosu prom enljive broj. Sve ispravno radi dok postoji uzajam no isključiva
brava (m utex) koja sinhronizuje pristupanje objektu Broj. Imajte u vidu da m etoda
Broj.povecajZaJedan( ) pom oću prom enljive p riv r i m etode y ield ( ) n am erno povećava
m ogućnost otkazivanja. U pravim višenitnim problem im a, m ogućnost otkazivanja m ože
biti statistički mala, pa ćete lakše 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 pažljivi kada pregledate program e za paralelno izvršavanje.
Vežba 17: (2) N apravite Gajgerov brojač koji m ože im ati proizvoljan broj daljinskih
senzora.

Gašenje 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 doći na vrh petlje,
gde nakon provere indikatora cancelled može da izađe iz nje. M eđutim , spavanje prouz-
rokovano m etodom s le e p () sam o je jedna od situacija u kojim a je izvršavanje zadatka
blokirano, a ponekad se i zadatak koji je blokiran m ora ugasiti.

Stanja niti
Nit m ože 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 izvršavanje ili blokirana.
2. Sprem na za izvršavanje: Znači da se nit m ože pokrenuti kada m ehanizam podele
procesorskog vrem ena bude im ao slobodnih procesorskih ciklusa za nju. M ožda se
ta nit izvršava, a m ožda i ne, ali ništa ne m ože sprečiti njeno izvršavanje ukoliko joj
m ehanizam za raspodelu vrem ena dodeli procesor. D rugim rečima, nit nije ni m rt-
va ni blokirana.
3. Blokirana: Nit bi mogla da se izvršava, ali je nešto sprečava. D ok je nit blokirana,
m ehanizam za raspodelu vrem ena je preskače i ne dodeljuje joj procesor. Sve dok
ta nit ponovo ne postane sprem na za izvršavanje, neće biti pokretana.
4. M rtva: M rtva ili ugašena (engl. term inated) nit više nije podobna za raspoređivanje
i procesorsko vrem e joj se ne dodeljuje. Njen zadatak je završen i ona se više ne
može pokrenuti. Zadatak norm alno um ire kada se vrati iz svoje m etode r u n ( ), ali
i njegova nit m ože biti prekinuta, kao što ćete uskoro videti.
946 Misliti na Javi

Prelazak u stanje blokiranosti


Z adatak m ože postati blokiran iz jednog od sledećih razloga:
• Uspavali ste taj zadatak pozivom m etode sleep(m ilisekundi) i on se neće izvršavati
tokom zadatog vremena.
• Zaustavili ste izvršavanje niti m etodom w a it( ). N it neće postati sprem na za izvrša-
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 bjasniću to u jednom od kasnijih odeljaka.
• N it čeka da se završi neka ulazno/izlazna operacija.
• N it pokušava da pozove neku sinhronizovanu m etodu drugog objekta koji je
zaključan.
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 (pošto su umele
da izazovu uzajam nu blokadu), pa ih u ovoj knjizi neću opisivati. Zastarela je i m etoda
s to p ( ) zato što ne otključava brave koje je n it zaključala, pa ako su objekti u nedoslednom
stanju (,,oštećeni“ ), ostali zadaci ih u tom stanju m ogu videti i modifikovati. Iz toga se
mogu izroditi suptilni problem i koji se teško otkrivaju.
R azm otrićem o sada ovaj problem : ponekad treba ugasiti zadatak koji je blokiran. Ako
ne m ožete sačekati da on dođe do mesta u kodu gde može da proveri neko stanje i sam
odluči da se ugasi, m orate ga p rim orati da izađe iz stanja blokiranosti.

Prekidanje izvršavanja
Kao što m ožete zamisliti, m nogo je teže izaći iz m etode R unnable.run( ) pre njenog kraja
nego sačekati da ona dospe do mesta gde ispituje vrednost indikatora ,,otkaži“ ili do ne-
kog drugog m esta gde je program er sprem an da napusti tu m etodu. Kada izađete iz blo-
kiranog zadatka, često treba da počistite resurse. Zato izlazak iz m etode r u n ( ) datog
zadatka pre njenog kraja, najviše liči 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,
pošto znači da ih često upotrebljavate za kontrolu toka program a.) Da biste se prilikom
ovakvog gašenja zadatka vratili na neko od poznatih dobrih stanja, m orate pažljivo raz-
m otriti putanje izvršavanja koda i napisati odredbu catch tako da pravilno sve počisti.
Klasa Thread sadrži m etodu in te rru p t( ) koja služi za gašenje blokiranih zadataka. O na
niti daje status prekinute. Nit kojoj je postavljen status prekinuta baca izuzetak Interrup-
tedException ukoliko je već blokirana ili pokušava da izvrši operaciju koja prouzrokuje
blokiranje. Status prekinuta biće 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( ) pruža
drugi način izlaska iz petlje r u n ( ), bez bacanja izuzetka.

16 M eđ u tim , izuzeci se nikada ne generišu asinhrono. Stoga n en ia o p asn osti da nešto prekine instruk-
ciju/poziv m eto d e. Ukoliko p rilikom u p o treb e m utexa (za razliku o d rezervisane reči sy n c h ro n ized )
b u d ete upotrebljavali idiom try-fin ally, te u zajam no isključive brave (m utex i) biće autom atski ot-
k ljučane kada se baci izuzetak.
Poglavlje 21: Paralelno izvršavanje 947

Da biste pozvali in te rru p t( ), m orate im ati nit, tj. objekat tipa Thread. M ožda ste
uočili da nova biblioteka concurrent (za paralelno izvršavanje) kao da izbegava neposre-
dan rad s nitim a; ona sve pokušava da ostvari izvršiocima. Ako pozovete shutdow n-
N ow ( ) za nekog izvršioca, on će poslati poziv in te rru p t( ) svim nitim a koje je pokrenuo.
To im a smisla, pošto najčešće hoćem o odjednom da ugasim o sve zadatke odredenog
izvršioca, kada završimo deo projekta ili ceo program . M eđutim , im a slučajeva kada bi-
sm o da ugasim o sam o jedan zadatak. Ako radite sa izvršiocem, kontekst zadatka koji ste
pokrenuli m ožete da uhvatite tako što ćete pozvati m etodu su b m it( ) um esto m etode
execute( ). s u b m it( ) vraća generički objekat Future<?> s nespeđficiranim param etrom ,
zato što za taj objekat nikada n'ećete zvati g e t( ) - svrha posedovanja ovakvog objekta tipa
Future jeste da za njega m ožete pozvati cancel( ) i tako ga upotrebiti za prekidanje
određenog 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 nači-
na prekidanja niti koju je pokrenuo izvršilac.
U narednom prim eru videćete osnove upotrebe m etode in te rru p t( ) u izvršiocu:

//: 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 otključava bravu
Thread.yield();
}
public SinhronizovanoBlokiranje() {
new T h r e a d O {
public void run() {
f(); // Ova nit zaključava bravu
}
}.start();
}
public void run() {
print("Pokušavam 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 slučaju izvršavanja
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); // ... pošto 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 izvršavanje 949

Pokušavam da pozovem f()


Preki dam Si nhroni zovanoBloki ranje
Prekid poslat objektu SinhronizovanoBlokiranje
Prekidam uz System.exit(0)
* ///:-

Svaki zadatak predstavlja različitu vrstu blokiranja. BlokiranoSpavanjem je prim er


blokiranja koje se m ože prekinuti, dok BlokiranoUIOperacijom i SinhronizovanoBlo-
kiranje nije m oguće 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, pošto 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 eđutim , da bism o pokazali SinhronizovanoBlokiranje, najpre m oram o za-
ključati bravu. To postižem o u konstruktoru, tako što se napravi instanca an o n im n e klase
Thread koja zaključava objekat pozivom m etode f ( ) (nit m ora biti neka druga a ne ona
koja izvršava r u n ( ) za SinhronizovanoBlokiranje, zato što jedna nit m ože više puta
zaključati objekat). Pošto se f ( ) nikada ne vraća, ta brava se nikada ne otključava.
SinhronizovanoBIokiranje.run( ) pokušava da pozove f ( ) i blokirano je dok čeka da
brava bude otključana.
Iz rezultata vidite da poziv m etode sleep( ) možete prekinuti (kao i svaki drugi poziv
koji zahteva hvatanje izuzetka InterruptedException). M edutim , ne m ožete prekinuti
zadatak koji pokušava da zaključa sinhronizovanu bravu, niti objekat koji pokušava da
obavi ulazno/izlaznu operaciju. To je pom alo uznem irujuće, naročito ako pišete zadatak
koji treba da obavlja ulazno/izlazne operacije, pošto to znači da one m ogu da zaključaju
višenitni program . To je razlog za brigu, pogotovo ako se radi o program im a za Web.
Dosta složeno, ali ponekad efikasno rešenje 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 podržavaju 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 podržavati 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(); // Oslobađa blokiranu nit
TimeUnit.SECONDS.sleep(l);
print("Zatvaram " + System.in.getClass().getName());
System.in.close(); // Oslobađa 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 naglašava se da se zadaci deblokiraju posle zatvaranja odgovarajućih re-
sursa. M ožda ste prim etili da se in te rru p tf ) pojavljuje kada zatvaram o Socket, ali ne i
kada zatvaram o System.in.
Srećom, civilizovaniji prekid ulaz.no/izlaznih operacija om ogućuju 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 izvršavanje 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 možete izvršiti i zatvaranjem pripadnog kanala, m ada bi
to retko trebalo da bude neophodno. Vodite računa 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
Vežba 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.
Vežba 19: (4) Izmenite program U krasnaB asta.java tako da upotrebite m etodu
in te r r u p t( ).
Vežba 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 završeni.

Pri istraživanju građe za ovaj odeljak pom ogao m i je E rvin Varga.


952 Misliti na Javi

Blokiranost koju izaziva uzajamno isključiva brava (mutexj


Kao što ste videli u program u P rekidanje.java, ako pokušate da pozovete sinhronizovanu
m etodu za zaključani objekat, pozivajući zadatak će biti zaustavljen (blokiran) dok se ob-
jekat ne otključa. U sledećem p rim eru videćete kako jedan zadatak m ože više puta
zaključati istu uzajam no isključivu bravu (m utex):

//: paralelno/VisePutaZakljucanaBrava.java
// Nit može više puta da zaključa 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. Pošto je zadatak već zaključao bravu objekta visePuta-
Z akljucanaB rava unutar prvog poziva m etode f l ( ), isti zadatak je ponovo zaključava u
Poglavlje 2 1: Paralelno izvršavanje 953

pozivu m etode f 2 ( ) itd. To im a smisla zato što bi zadatak trebalo da može da poziva sin-
hronizovane m etode u n u tar istog objekta; taj zadatak je već zaključao objekat (bravu).
Kao što je već bilo rečeno za ulazno/izlazne operacije koje nije moguće prekinuti, kad
god zadatak m ože biti tako blokiran da ga se ne m ože prekinuti, postoji m ogućnost da će
to zaključati ceo program . U biblioteke za paralelno izvršavanje Jave SE5 dodata je i m o-
gućnost prekidanja zadataka blokiranih na bravam a (objektim a) tipa R eentrantL ock, za
razliku od zadataka blokiranih na sinhronizovanim m etodam a ili kritičnim delovima:

//: paralelno/Prekidam2.java
// Prekidam zadatak blokiran pomoću 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 zaključati, 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 neće 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("zaključana brava u f ()");
} catch(InterruptedException e) {
p r i n t ("Prekinuto u pokušaju zaključavanja 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 ("Izašao 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 pokušaju zaključavanja brave u f()
Izašao iz blokiranog poziva
* ///•-
Klasa BlokiranMutex im a k onstruktor koji zaključa bravu (objekat tipa Lock) samog
objekta i nikada je ne otključa. Zbog toga ćete, ukoliko pokušate da pozovete f ( ) iz nekog
drugog zadatka (različitog od onoga koji je napravio objekat BlokiranMutex), uvek biti
blokirani pošto je ta uzajam no isključiva brava stalno zaključana. U klasi Blokirana2,
m etodu r u n ( ) zaustaviće poziv m etode b lo k irana.f( ). Kada pokrenete program , videćete
da, za razliku od poziva ulazno/izlaznih operacija, in te rru p t( ) m ože da izađe iz poziva
koji je blokirala uzajamno isključiva 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 sluča-
ju da zadatak uđe u operaciju koja blokira ili je već u n u tar nje (sem, kao što ste videli, u
slučaju ulazno/izlaznih operacija koje nije m oguće prekinuti, i!i blokiranih sinhroni-
zovanih m etoda - tada ste bespom oćni). Ali šta ako kod takav blokirajući poziv pravi
sam o u zavisnosti od uslova u kojim a se izvršava? Ukoliko m ožete da izađete samo tako
što ćete baciti izuzetak na blokirajući poziv, nećete uvek moći da izađete 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
način da izađe, za slučaj da petlja m etode r u n ( ) ne napravi nijedan blokirajući poziv.
Tu m ogućnost pruža 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 briše status preki-
nutosti. Kada se izbriše status prekinutosti, obezbeđuje se da osnovna stru k tu ra (engl.
framevvork) ne obaveštava dvaput o tom e da je neki zadatak prekinut. Bićete obavešteni
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 vraća
tru e. Ukoliko želite ponovo da proverite da li je nastao prekid, sačuvajte rezultat poziva
m etode T h re a d .in te rru p te d ().
U narednom prim eru prikazan je tipičan 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
// Opšti 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 izvršavanje 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()} {
// tačkal
TrebaPocistiti nl = new TrebaPocistiti(1);
// Blok try-firially počnite neposredno iza definicije nl,
// da bi bilo zajemčeno pravilno čišćenje nl:
try {
printC'Spavam");
Timellnit.SECONDS.sleep(l);
// tačka2
TrebaPocistiti n2 = new TrebaPocistiti(2);
// Zajemčeno pravilno čišćenje n2:
try {
print("Računam");
// Dugotrajna operacija koja ne blokira:
for(int i = 1; i < 2500000; i++)
d = d + (Math.PI + Math.E) / d;
print("Završena dugotrajna operacija ");
} final ly {
n2.cleanup();
}
} finally {
nl.cleanupO;

print("Izlazim iz niti ispitivanjem uslova od while()");


} catch(InterruptedException e) {
print("Izlazim iz niti pomoću izuzetka InterruptedException");
}
}
}

public class IdiomPrekidanja {


public static void main(String[] args) throws Exception {
if(args.1ength != 1) {
pri nt("pokretanje: java IdiomPrekidanja kašnjenje_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
Računam
Završena dugotrajna operacija
Čistim 2
Čistim 1
TrebaPocistiti 1
Spavam
Čistim 1
Izlazim iz niti pomoću izuzetka InterruptedException
* ///:-

Klasa TrebaPocistiti naglašava potrebu za pravilnirn čišćenjem resursa ukoliko petlju


napustite putem izuzetka. Vodite računa 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 kašnjenja u milisekun-
dam a - pre nego što pozove in te rru p t( ). Korišćenjem različitih kašnjenja možete izaći iz
m etode B lokirano3.run( ) na različitim tačkam a petlje: u pozivu m etode sleep( ) koja
blokira ili u m atem atičkom p roračunu koji ne blokira. Ukoliko interrupt( ) bude pozva-
na nakon kom entara ,,tačka2“ (tokom operacije koja ne blokira), videćete da se prvo
dovršava petlja, zatim se uništavaju svi lokalni objekti i najzad se izlazi iz petlje preko na-
redbe while na njenoni vrhu. M eđutim , ako se in te rru p t( ) pozove između „taćkel" i
,,tačke2“ (nakon naredbe while, ali pre ili u toku operacije sleep( ) koja blokira), zadatak
izlazi pom oću izuzetka InterruptedException čim se prvi put pokuša operacija koja blo-
kira. U tom slučaju, čiste se samo objekti klase TrebaPocistiti napravljeni do trenutka ba-
canja izuzetka, a sve ostalo čišćenje možete obaviti u n u tar odredbe catch.
Klasa koja odgovara na in te rru p t( ) m ora da ustanovi strategiju kojom obez.beđuje
svoj ostanak u konsistentnom (neoštećenom ) stanju. Po pravilu, to znači da iza pravljenja
svih objekata koji zahtevaju čišćenje m oraju slediti odredbe try-finally, da bi se ćišćenje
obavilo bez obzira na način izlaska iz petlje r u n ( ). Takav kod može dobro da radi, ali
nažalost - zato što u Javi nem a autom atskih poziva destruktora - zavisi od toga da li će
program er klijent dobro napisati odredbe try-finally.

Međusobna saradnja zadataka


Kao što ste videli, kada pom oću niti istovrem eno izvršavate više zadataka, sprečićete je-
dan zadatak da pokvari resurse drugog ako bravom (m utexom ) sinhronizujete njihovo
ponašanje. D rugim rečima, ako se dva zadatka sudaraju zbog deljenog resursa (najčešće
m em orije), za pristupanje tom resursu upotrebite uzajam no isključivu bravu (m utex).
Poglavlje 2 1: Paralelno izvršavanje 957

Kada je taj problem rešen, sledeći korak je naučiti kako da zadaci sarađuju jedan s dru-
gim, da bi više zadataka m oglo zajednički da reši problem . Sada se ne postavlja pitanje
kako da se zadaci m edusobno ne om etaju, nego kako da rade usaglašeno, pošto se takvi
p roblem i m oraju rešavati postepeno, utvrđenim redosledom . Kao u građevinarstvu: naj-
pre se m o ra iskopati rupa za temelje, ali se arm atura m ože položiti paralelno sa pra-
vljenjem betonskih blokova, i oba ta zadatka m oraju biti dovršena da bi se nalili temelji.
Cevi m oraju biti položene pre nalivanja betonske ploče, ploča m ora biti nalivena pre nego
što počne podizanje zidova itd. Neki od ovih zadataka m ogu se izvršavati paralelno, ali
određeni koraci ne m ogu se izvesti ukoliko svi zadaci nisu završeni.
U m eđusobnoj saradnji zadataka glavno pitanje je prim opredaja signala. Za tu p rim o-
predaju upotrebljavam o zajednički temelj: uzajam no isključivu bravu (m utex) koji u
ovom slučaju jem či da će na određeni signal odgovoriti sam o jedan zadatak. Tim e se iz-
begavaju svi eventualni uslovi za trku. Pored mutexa, dodaćem o način da se zadatak zau-
stavi do k se ne prom eni neko spoljno stanje (npr. „Cevi su položene“), koje pokazuje da
je vrem e da se taj zadatak pokrene. U ovom odeljku, razm otrićem o prim opredaju signala
izm eđu zadataka, koju bezbedno realizuju m etode w a it( ) i notifyA ll( ) klase Object. Bi-
blioteka za paralelno izvršavanje Jave SE5 ima i klasu Condition, s m etodam a aw ait( ) i
s ig n a l(). Videćemo koji problem i m ogu nastati i kako se rešavaju.

wait() i notifyAII()
M etoda w a it( ) se obično koristi dok čekate na prom enu uslova čije ispunjenje određuju
sile kojim a ne upravlja tekuća m etoda. Cesto neki drugi zadatak m enja taj uslov. Ne želite
da program stoji besposleno u niti, stalno ispitujući taj uslov; to se naziva zauzetost če-
kanjem (engl. busy vvaiting) i obično pređstavlja loš način korišćenja procesorskog vrem e-
na. Stoga w a it( ) om ogućava da uspavate tu nit dok čeka da se svet prom eni. O na se budi
i ispituje prom ene tek kad naiđe na m etode n o tify( ) ili notifyA ll( ) koje nagoveštavaju da
se m ožda desilo nešto važno. Tako je dobijen još jedan način sinhronizacije niti.
Važno je shvatiti da ni sleep( ) niti yield( ) ne otključavaju objekat kada ih pozovete.
S druge strane, kada zadatak iz neke m etode pozove m etodu w a it( ), ona otključava obje-
kat pre nego što zaustavi izvršavanje niti. Pošto w a it( ) otključava bravu čim je pozovete,
to znači da drugi zadaci mogu da pribave tu bravu, pa tokom izvršavanja m etode w a it ( )
možete da pozivate druge sinhronizovane m etode tog (sada otključanog) objekta. To je
neophodno, pošto obično baš te druge metode prouzrokuju p rom enu koju zaustavljena
nit čeka da bi se probudila. Dakle, kada pozovete w a it( ), saopštavate: „Zasad sam uradio
sve što sam mogao, pa ću na ovom m estu da pričekam , ali hoću da om ogućim drugim
sinhronizovanim operacijam a da se izvršavaju ako mogu.“
Postoje dva oblika m etode wait( ). Prvi prim a argum ent u milisekundam a, čije je
značenje 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), dešava se sledeće:
1. O tključava se objekat tokom čekanja izvršavanja m etode w a it( ).
2. Iz m etode w a it( ) možete da izađete i zbog obaveštenja koje šalju m etode n o tify () ili
notifyA U (), ili će se izvršavanje samo nastaviti po isteku zadatog vrem ena čekanja.
958 Misliti na Javi

Drugi, češće upotrebljavani oblik m etode wait( ) ne prim a nikakve argum ente, što
znači 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 završava 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 nešto što služi isključivo za niti b ude pripadnik univerzalne osnovne klase - ali je
neizbežno, jer te m etode rade s bravam a koje su takođe deo svakog objekta. Zato w a it( )
m ožete da smestite u svaku sinhronizovanu m etodu, bez obzira na to da li u toj konkret-
noj klasi postoji ili ne postoji više niti, tj. da li je izvedena iz klase Thread ili realizuje in-
terfejs Runnable. U stvari, w a it( ), n o tify ( ) i notifyAll( ) možete da pozovete jedino iz
sinhronizovanih m etoda ili blokova (sleep( ) možete pozivati i iz nesinhronizovanih me -
toda, pošto 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 izvršavanja dobiti
izuzetak tipa IllegalM onitorStateException uz pom alo nerazum ljivu poruku „current
thread not ow ner“ (tekuća nit tren u tn o nije vlasnik brave objekta s kojim hoće da radi).
Poruka znači da zadatak koji poziva w a it( ), notify( ) ili notifyA ll( ) m ora zaključati bravu
objekta pre nego što pozove jed n u od tih metoda.
M etode w a it( ), n o tify ( ) ili notifyA ll( ) možete da pozivate sam o za svoje brave. Nema
petljanja s tuđim bravama, ali od drugog objekta možete zatražiti 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 pošaljete obaveštenje notifyAll( ), to možete uraditi u n u ta r sinhro-
nizovanog bloka koji pribavlja bravu od x, tj. zaključava x:

synchronized(x) {
x.notifyAll();
}

Razm otrićem o jednostavan prim er. VoskOMatik.java ima dva procesa: nanošenje
voska na Kola i poliranje. Poliranja nem a dok se ne završi nanošenje voska, a zadatak na-
nošenja m ora da sačeka dok se zadatak poliranja završi - tek onda se može naneti sledeći
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 pokreće zadatke dok oni čekaju da se prom eni
određeni uslov:

//: paralelno/voskomatik/VoskOMatik.java
// Osnove međusobne 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 izvršavanje 959

public synchronized void polirano() {


voskalma = false; // Spremno za nanošenje sledećeg 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 pomoću prekida");
}
print("Završavam 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 pomoću prekida");
}
print("Završavam 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
Završavam zadatak Voska Ima
Izlazim iz niti pomoću prekida
Završavam zadatak Voska Nema
* ///:-

Kola im aju samo jednu prom enljivu tipa boolean pod im enom voskalm a koja po-
kazuje stanje procesa nanošenja voska i poliranja.
U m etodi cekajNaVoskiranje( ) proverava se indikator voskalm a, i ako je njegova
vrednost false, pozivajući zadatak se zaustavlja tako što se poziva m etoda w a it( ). Važno
je da se to dešava u sinhronizovanoj m etodi, gde je zadatak pribavio bravu. Kada pozovete
w a it( ), nit se zaustavlja, a brava otključava. N eophodno je da brava bude otključana zato
što drugi zadaci m oraju im ati m ogučnost da je pribave (zaključaju) 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 uopšte dobio
priliku da nastavi rad). U ovom prim eru, kada drugi zadatak pozove voskirano( ) da bi
pokazao kako je vreme da se nešto uradi, brava m ora biti pribavljena (zaključana) 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 zaključa
bravu koju je otključao kada je ušao u taj w a it( ). Zadatak se neće probuditi dok ta brava
ne postane dostupna, tj. otključana.20

20 Na nckim platfo rm am a postoji i treći naćin izlaska iz m etode w a it(): to je takozvano shičajno
budetije. U suštini, slučajno 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.
Slučajno 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 lakši ako se proglasi da slučajna bu denja ,,ne sm etaju toliko“.
Poglavlje 21: Paralelno izvršavanje 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 završeno 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 , videćete 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 kontroliše.
U p retho dn om prim eru naglašeno je da poziv m etode w a it() m orate staviti u petlju
w hile koja proverava odgovarajuće uslove. To je važno zato što:
• M ožete imati više zadataka koji iz istog razloga čekaju na istu bravu, a prvi zadatak
koji se probudi može prom eniti tu situaciju (čak i ako to sami ne uradite, to može
svako ko je izveo svoju klasu iz vaše). U tom slučaju, taj zadatak treba ponovo zau-
staviti dok se njegov odgovarajući uslov ne prom eni.
• Kada se zadatak probudi iz svog čekanja —m etode w a it() - m ožda su drugi zadaci
prom enili situaciju tako da ovaj zadatak ne može 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 oguće je i da zađaci čekaju na bravu tog objekta iz različitih razloga, a tada morate
upotrebiti notifyAll( ). U tom slučaju, m orateproveriti da li je zadatak probuđen iz
pravog razloga, i ako nije, ponovo pozovite w a it().
Dakle, n eophodno je da proveravate ispunjenost odgovarajućeg uslova i da se vraćate
u w a it( ) ako taj uslov nije ispunjen. To se obično piše pom oću petlje while.
Vežba 21: (2) Napravite dve klase koje realizuju interfejs Runnable, jednu s m etodom
r u n () koja na svom početku 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 prikaže poruku. Ispitajte klase po-
m oću izvršioca (Executor).
Vežba 22: (4) Napravite prim er zauzetosti čekanjem. Jedan zadatak neko vrem e spava i
zatim postavlja vrednost određenog 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, zađaje 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().

Propušteni signali
Kada je rad dve niti usklađen m etodam a notify( )/w a it() ili notifyAH( )/w a it(), može se
propustiti signal. Pretpostavim o da je N1 nit koja obaveštava nit N2 i da su obe realizo-
vane na sledeći (pogrešan) način:
962 Misliti na Javi

N1 :
synchronized(deljeniMoni tor) {
<pripremo u slo v a za N2>
deljeniMonitor.notify();
}

N2:
while(nekillslov) {
// Tačka 1
synchronized(deljeniMonitor) {
deljeniMonitor.wait();
}
}
<priprema uslova za N2> treba da spreči N2 da zove w a it(), ako to N2 već nije učinila.
Pretpostavim o da N2 izračuna nekiUsIov i dobije rezultat true. U Tačkil, m ehanizam
za raspodelu procesorskog vrem ena može prebaciti na nit N l. N1 izvršava svoju pripre-
m u i zatim poziva n o tify (). Kada N2 nastavi da se izvršava, za nju je prekasno da shvati
kako se uslov u m eđuvrem enu prom enio, i ona slepo ulazi u w a it(). Poruka m etode
n o tify () biče propuštena i N2 će čekati beskonačno dugo na signal koji je već bio poslat;
dobili sm o uzajam nu blokadu (engl. deadlock).
Rešenje je da se spreči uslov za trku prim en om prom enljive nekiUslov. Ovo je ispra-
van način realizacije N2:

synchronized(deljeniMonitor) {
while(nekiUslov)
deljeniMonitor.wait();
}

U ovoj realizaciji važi sledeće: ako nit N1 prva dobije priliku da se izvršava, kada se
kontrola vrati niti N2 ona će shvatiti da se uslov prom enio i neće ući u w a it(). U protiv-
nom , ukoliko nit N2 prva dobije priliku da se izvršava, ući će u w a it() i kasnije će je pro-
buditi N l. Dakle, signal ne može biti propušten.

notify() u odnosu na notifyAII()


Strogo uzev, više zadataka može čekati - m etodom w a it() - na isti objekat tipa Kola, pa
je bezbednije pozvati n otifyA ll() um esto n o tify (). M eđutim , 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().
Korišćenje n o tify () um esto notifyA H () predstavlja optim izaciju. Od svih mogućih
zadataka koji čekaju na određenu bravu, n o tify () će probuditi sam o jedan, pa ako
pokušate 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 različite uslove, ne m ožete 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 ograničenja m oraju uvek važiti za sve m oguće potklase. Ukoliko bilo koje od
ovih pravila ne može biti zadovoljeno, m orate koristiti n o tify A ll(), a ne n o tify ().
Poglavlje 21: Paralelno izvršavanje 963

U raspravama o višenitnom izvršavanju u Javi često se ponavlja zbunjujuća tvrdnja da


n otifyA ll() budi ,,sve zadatke koji čekaju“. Da li to znači 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 određenu 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 način 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 izvršavanje 965

podešen tako da svoju m etodu r u n ( ) izvršava svake 4/10 sekunde, i ta m etoda r u n ()


n aizm enično poziva n o tify () i n otify A ll() za Zadatak.blokator m etodam a ,,probudi“.
Iz rezultata uočavate sledeće: 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 buđenje 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 izvršava 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 znači da same pribavljaju (zaključavaju) svoju bravu, pa
kada pozivaju n o tify () ili n otify A ll(), logično 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 napišem sam o for(;;)
um esto w hile(!T hread.interrupted()) i u ovom slučaju postignem isti rezultat; to je zato
što u ovom prim eru nem a razlike izm eđu napuštanja petlje pom oću izuzetka i na-
puštanja preko provere indikatora in terrup ted() - u oba slučaja izvršava se isti kod.
M eđutim , u ovom prim eru se form e radi proverava interrupted( ), pošto postoje dva
različita načina za napuštanje petlje. Ukoliko kasnije odlučite da dodate još koda u petlju,
rizikujete da napravite grešku ako ne uzm ete u obzir obe putanja izlaska iz petlje.
Vežba 23: (7) Pokažite da VoskO M atik.java dobro radi i kada upotrebite n o tif y ( ) um e-
sto n o tify A II().

Proizvođači i potrošači
Zam islite restoran s jednim kuvarom i jednim konobarom . Konobar m ora da sačeka dok
kuvar prip rem i jelo. Kuvar obaveštava konobara kada je jeio zgotovljeno. Tada konobar
uzim a jelo, poslužuje ga pa se vraća i čeka. To je prim er saradnje zadataka: kuvar predsta-
vlja proizvođača, a konobar potrošača. Zadaci m oraju da razm enjuju signale tokom
proizvodnje i potrošnje jela, i sistem m ora da se uredno ugasi. Ovo je ta priča oblikovana
kao kod:

//: paralelno/Restoran.java
// Saradnja zadataka kao saradnja proizvođača i potrošača.
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 sledeće
}
}
} 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 posluženo
}
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 izvršavanje 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 žiža i za Konobara i za Kuvara. O ba m oraju znati za koji Restoran rade,


pošto 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 režim čekanja - w a it() - i zausta-
vlja taj zadatak dok ga ne probudi poruka n o tify A ll() od Kuvara. Pošto 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 eđutim , u
složenijim situacijam a, na bravu određenog objekta može čekati više 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 određenu bravu. Potom svaki od tih zadataka m ora odlučiti da li je
to obaveštenje 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 može da proizveđe
sledeće Jelo.
O bratite pažnju 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 (spavajući) na narudžbu i budete
probuđeni, to m ora značiti da postoji nova narudžba, zar ne? Već sam rekao da je problem
to što u paralelnoj aplikaciji neki drugi zadatak može da uleti i ugrabi narudžbu dok se Ko-
nobar budi. Jedini bezbedan način jeste da za w a it() uvek upotrebljavate sledeći idiom (uz
pravilnu sinhronizaciju, naravno, i program iranje koje sprečava propuštanje signala):

whi1e(uslovNijelspunjen)
wai t();
968 Misliti na Javi

Time se jem či da če uslov biti ispunjen pre nego što izađete iz petlje čekanja, i ako ste
obavešteni o nečem u što nem a veze sa uslovom - što se m ože desiti kada je poruka
n o tifyA ll() - ili se uslov pro m eni pre nego što p o tp u n o izađete iz petlje čekanja, svakako
ćete se vratiti u čekanje.
Vodite računa o tom e da poziv n o tify A ll() m ora najpre zaključati bravu konobara.
Poziv m etode w a it() iz m etode K onobar.run() autom atski otkJjučava bravu, pa je to
moguće. Pošto brava m ora biti zaključana da bi n o tify A ll() m ogla biti pozvana, dva
zadatka koji pokušavaju da pozovu n o tify A ll() za isti objekat zajemčeno neće 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 završava se neposredno pre završne vitičaste zagrade m etode r u n (), pa
ako zadatak prim i InterruptedException, završiće se o dm ah čim uhvati izuzetak.
U klasi Kuvar, obratite pažnju na to da bism o nakon poziva m etode sh utdow n N ow ()
običnom naredbom return mogli da se jednostavno vratim o iz m etode r u n ( ), i to
najčešće i treba da uradite. M eđutim , 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 slučaju klase Kuvar, zadatak se ne gasi neposredno po prijem u poruke in terru p tf),
pošto prekid m ože da generiše izuzetak InterruptedException jedino kada zadatak
pokuša da uđe u (neprekidnu) operaciju koja blokira. Zato se najpre prikazuje „Evo
narudzbe!“ i zatim baca izuzetak InterruptedException kada Kuvar pokuša da pozove
sle e p (). Ako uklonite poziv m etode sle e p (), zadatak će doći na vrh petlje r u n () i izaći
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 ože da smesti objekat koji će
drugi zadatak kasnije upotrebiti. M eđutim , u tipičnoj realizaciji proizvodača i potrošača,
za skladištenje objekata koji se proizvode i troše prim enjuje se princip FIFO (fir$t-inyfirst-
out —prvi izlazi onaj koji je prvi ušao). Više reči o takvim redovim a za čekanje biće u na-
stavku poglavlja.
Vežba 24: (1) M etodam a w a it() i n o tify A ll() rešite problem jednog proizvođača i jed-
nog potrošača. Proizvođač ne sme da prepuni prim aočev bafer, što se može desiti ukoliko
je proizvođač brži od potrošača. Ako je potrošač brži od proizvođača, onda isti podatak
treba da pročita samo jednom . Ne sm ete ništa pretpostaviti o relativnim brzinam a
proizvođača i potrošača.
Vežba 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 ponašanju.
Vežba 26: (8) Program u Restoran.java dodajte klasu Pom ocnik. Kada posluži jelo, Ko-
nobar treba da obavesti P om ocnika da počisti.

Korišćenje eksplicitnih objekata tipa Lock i Condition


U biblioteci Java SE5 java.util.concurrent ima i drugih, eksplicitnih alatki pom oću kojih
m ožem o preraditi program VoskOMatik.java. O snovna klasa koja koristi uzajam no
isključivu bravu (mutex) i om ogućuje zaustavljanje zadataka nazvana ie Condition.
Poglavlje 2 1: Paralelno izvršavanje 969

Zadatak zaustavljate pozivom m etode a w ait() za objekat tipa Condition. Kada nastanu
spoljne prom ene stanja koje m ožda znače da neki zadatak treba da nastavi izvršavanje, taj
zadatak obaveštavate 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 sadrži C ondition po-
m oću kojeg se određeni 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 nanošenje sledećeg 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 pomoću prekida");
}
print("Završavam 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 pomoću prekida");
}
print("Završavam zadatak Voska Nema");
}

public class VoskOMatik2 {


public static void main(String[] args) throws Exception {
Kola kola = new Kola();
Poglavlje 21: Paralelno izvršavanje 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 pomoću prekida
Za vr ša va m zadatak Voska Nema
Izlazim iz niti pomoću prekida
Za vršavam zadatak Voska Ima
* ///:-

U ko nstruktoru Kola, jedna brava (objekat tipa Lock) proizvodi uslov, tj. objekat tipa
C ondition pom oću kojeg se upravlja kom unikacijom izm eđu zadataka. M eđutim , obje-
kat C ondition ne sadrži 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 slučajevima ostvariti otključavanje. Kao u ugrađenim verzijama,
zađatak m ora zaključati 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 rešenje složenije nego prethodno, a ta složenost u ovom slučaju
nije donela ništa dobro. Objekti tipa Lock i Condition potrebni su sam o za teže problem e
višenitnog izvršavanja.
Vežba 27: (2) Izmenite Restoran.java tako da korisli eksplicitne objekte tipa Lock i
Condition.

Proizvođači, potrošači i redovi za čekanje


M etode w a it() i n otifyA ll() rešavaju problem saradnje zadataka na prilično niskom
nivou, razm enjujući signale u svakoj interakciji. U m nogo slučajeva, m ožete se popeti za
jedan nivo apstrakcije i problem e saradnje zadataka rešiti pom oću 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 sadrži interfejs java.util.concurrent.Blocking-
Queue koji ima više standardnih realizacija. Najčešće ćete koristiti LinkedBlocking-
Queue, što je neograničen red za čekanje; ArrayBlockingQueue ima neprom enljivu
veličinu, pa u njega možete staviti sam o nevelik broj elem enata pre nego što se zablokira.
Tikav red za čekanje zaustavlja potrošački zadatak koji pokuša da uzm e objekat iz
praznog reda i nastavlja njegovo izvršavanje kada više elem enata postane dostupno. Blo-
kiranje redova za čekanje rešava veliki broj problem a na m nogo jeđnostavniji i pouzđaniji
način od m etoda w ait( ) i notifyA lI().
972 Misliti na Javi

Sledi jeđnostavan test koji serijalizuje izvršavanje Lansiranje objekata. Potrošač je


IzvršavaLansiranje, koji vadi svaki objekat Lansiranje iz blokirajućeg reda za čekanje
(BlockingQueue) i izvršava ga neposredno. (D rugim rečima, eksplicitnim pozivom me-
tode r u n () koristi sopstvenu nit, um esto da za svaki zadatak pokreće 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("Buđenje iz metode take()");
}
print("Izlazim iz niti IzvrsavaLansiranje");
}
}

public class TestBlokirajucihRedovaZaCekanje {


static void getkey() {
try {
// Zato što taster Enter proizvodi rezultat koji je
// različite dužine 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 izvršavanje 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("Završen " + prk + " test");
}
public static void main(String[] args) {
test("LinkedBlockingQueue", // Neograničena veličina
new LinkedBlockingQueue<Lansiranje>());
test("ArrayBlockingQueue", // Nepromenljiva veličina
new ArrayBlocking0ueue<Lansiranje>(3));
test("SynchronousQueue", // Veličine 1
new SynchronousQueue<Lansiranje>());
}
} ///:-
M etoda m a in () smešta zadatke u BlockingQueue, iz kojeg ih vadi m etoda Izvr-
savaLansiranje. O bratite pažnju na to da IzvrsavaLansiranje može da zaboravi na
sinhronizaciju zato što to rešava BlockingQueue.
Vežba 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 ().

Blokirajući redovi za čekanje tosta


Kao prim er korišcenja blokirajućih redova za čekanje (BlockingQueue), uzm im o
m ašinu 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 ože da se nalazi u blokirajućim
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 isključen");
}

// Namaži 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 sledeče parče tosta:
Tost n = redSuvih.takeO;
n.namaziMaslacem();
print(n);
redSMaslacem.put(n);
}
} catch(InterruptedException e) {
print("MazeMaslacem prekinut”);
}
Poglavfje 21: Paralelno izvršavanje 975

print("MazeMaslacem isključen");
}
}

// Namaži 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 sledeće parče tosta:
Tost n = redSMaslacem.take();
n.namaziPekmezomO;
print(n);
redGotovih.put(n);
}
} catch(InterruptedException e) {
print("MazePekmezom prekinut");
}
print("MazePekmezom isključen");
}
}

// Potroši 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 sledeće parče tosta:
Tost n = redGotovih.take();
// Proveri da li tost uredno dolazi,
// i da li sva parčad bivaju namazana pekmezom:
if(n.dajld() != brojac++ ||
n.dajStatus() != Tost.Status.SPEKMEZOM) {
p r i n t ( " » » Greška: " + n);
System.exit(l);
} else
print("Njam! " + n);
}
} catch(InterruptedException e) {
976 Misliti na Javi

print("Gost prekinut");
}
print("Gost isključen");
}
}

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 odličan prim er vrednosti nabrojanih tipova. Vodite računa o tom e da nema
eksplicitne sinhronizacije (ne upotrebljavaju se objekti tipa Lock niti rezervisana reč syn-
chronized), pošto sinhronizaciju im plicitno (interno) obavljaju redovi za čekanje i
projekat sistema - svako parče Tosta u svakom trenutku predm et je obrade sam o jednog
zadatka. Pošto redovi za čekanje blokiraju izvršavanje, procesi se zaustavljaju i nastavljaju
autom atski. N adam se da vidite kako pojednostavljenje koje su doneli blokirajuči redovi
za čekanje um e da bude veom a veliko. Izbegnute su veze izm eđu klasa koje bi postojale uz
eksplicitne naredbe w a it() i n o tifyA ll(), jer svaka klasa kom unicira samo sa svojim blo-
kirajućim redom za čekanje.
Vežba 29; (8) Izmenite TostOMatik.java tako da na dve zasebne linije pravi sendviče 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 između zadataka


Često je korisno da zadaci m eđusobno kom uniciraju pom oću ulazno/izlaznih operacija.
Biblioteke za višenitno izvršavanje podržavaju ulazno/izlazne operacije izm edu zadataka
u obliku cevi (engl. pipes). U Javinoj biblioteci za ulazno/izlazne operacije njih predsta-
vljaju klase PipedWriter (pom oću koje zađatak piše u cev) i PipedReader (pom oću koje
drugi zadatak čita iz iste cevi). To m ožete sm atrati varijacijom problem a proizvođača i
potrošača, uz cevovod kao gotovo rešenje. U suštini, cev je blokirajući red za čekanje koji
je postojao u verzijama Jave pre uvođenja klase BlockingQueue.
Sledi jednostavan prim er u kojem dva zadatka kom uniciraju pom oću cevi:

//: paralelno/UICevovod.java
// Ulazno/izlazna komunikacija zadataka kroz cevi
import java.util.concurrent.*;
Poglavlje 21: Paralelno izvršavanje 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 eđusobno 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 ridruženo određenom PipedWriteru u kon-
struktoru. Predajnik smešta podatke u Writer i odlazi na nasum ično dugo spavanje.
M eđutim , 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 pažnju na to da se predajnik i prijem nik pokreću 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 različito ponaša na raznim platform am a. (Vodite računa o tom e da su objekti tipa
BlockingQueue (blokirajući redovi za čekanje) robusniji i lakše se koriste.)
Važna razlika izm eđu objekta tipa PipedReader i norm alnih ulazno/izlaznih operacija
vidi se u pozivu m etode sh utdow n N ow () - PipedReaderse m ože 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 izađe iz poziva rea d ().
Vežba 30: (1) Izmenite UlCevovod.java tako da se um esto cevi koristi BlockingQueue.

Uzajamna blokada
Sada znate da objekat može im ati sinhronizovane m etode ili druge oblike zaključavanja
koji sprečavaju zadatke da pristupaju tom objektu dok se ne otkijuča uzajam no isključiva
brava (mutex). Saznali ste i da zadaci m ogu postati blokirani. Dakle, m oguće je da se je-
dan zadatak zaglavi čekajući na drugi, koji ćeka na treći itđ., dok lanac ne đođe do zadatka
koji čeka na onaj prvi zadatak - neprekidan lanac zadataka koji čekaju jedan na drugog i
nijedan od njih ne može da se pom eri. To se naziva uzajamna blokada (engl. deadlock).1'
Ako pokrenete program i on odm ah uđe u uzajam nu blokadu, grešku ćete odm ah
moći da pronađete. Pravi problem je kada program naizgled radi dobro, ali sadrži skrive-
nu m ogućnost uzajam ne blokade. U tom slučaju, uopšte ne m orate dobiti nikakvu na-
znaku da m ogućnost uzajam ne blokade postoji, pa će greška biti latentna u program u
dok se neočekivano ne desi kupcu (na način koji će gotovo sigurno biti teško ponoviti).
Zato je pažljivo projektovanje program a kako bi se sprečila uzajam na blokada, ključan
deo razvoja paralelnih sistema.
Klasičan prim er uzajam ne blokade jeste problem večercfilozofa, 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 išljanju, a deo u jelu. Dok
razmišljaju, ne koriste deljene resurse, ali za jelo imaju na raspolaganju ograničenu

' M o že n a s ta ti i uzajam na blokada uprkos izvršavanju, 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 išta k o risn o .
Poglav[je 21: Paralelno izvršavanje 979

količinu pribora. U originalnom opisu problem a, p rib o r za jelo su viljuške, i za vađenje


špageta iz činije na sredini stola potrebne su dve viljuške, ali m eni se čini da je prikladnije
da za p rib or za jelo proglasim o štapiče (kakvim a Istočnjaci jedu pirinač). Jasno, svakom
filozofu trebaju dva štapića za jelo.
Zaplet problem a je sledeći: budući da su filozofi, veom a su sirom ašni i im aju sam o pet
štapića (odnosno štapića im a onoliko koliko im a i filozofa). Stapići su ravnom erno
raspoređeni 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 štapića, naš filozof m o ra da sačeka dok potrebni štapić ne postane
dostupan.

//: paralelno/Stapic.java
// Štapići za večeru 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 određeni Stapic, drugi može m etodom w a it() da sačeka 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 drži ne pusti taj Stapic). Zatim zadatak daje
indikatoru zauzet vrednost true da bi pokazao da Stapic sada drži novi Filozof. Kada taj
Filozof završi jelo pom oću Stapica, on poziva m etodu p u s ti() da bi prom enio vrednost
tog indikatora i m etodu n otifyA ll() kojom obaveštava sve druge Filozofe koji m ožda če-
kaju na taj Stapic.

//: paralelno/Fi1ozof.java
// Filozof večera
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 + " " + "razmišlja");
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 pomoću 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 ično odabranog vrem enskog razdoblja ukoliko je te-
zinskiFaktor različit od nule. Dakle, F ilozof razmišlja tokom nasum ično odabranog vre-
m enskog razdoblja, zatim pokušava da uzm e desni i levi Stapic, jede tokom nasum ično
odabranog vrem enskog razdoblja i zatim ponavlja ceo postupak od početka.
Sada čem o napraviti verziju program a koja če se završiti uzajam nom blokadom :

//: paralelno/UzajamnoBlokiraniFilozofiKojiVeceraju.java
// Pokazuje kako u programu može biti prikrivena uzajamna blokada.
// {Args: 0 5 odmor}
import java.util .concurrent.*;
Poglavfje 2 1: Paralelno izvršavanje 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
izvršavanje programa");
System.in.read();
}
exec.shutdownNow();
}
} /* (Pokrenite da biste videli rezultat) *///:-

Prim etili ste sledeće: ukoliko Filozofi provode malo vrem ena razmišljajući, kada po-
kušaju da jedu svi će jedan drugom konkurisati za Stapice i uzajam na blokada će nastu-
piti m nogo brže.
Prvi argum ent na kom andnoj liniji zadaje težinski faktor ponder koji utiće na količinu
vrem ena koje svaki Filozof provodi u razmišljanju. Ako Filozofa im a m nogo ili oni vaz-
dan razmišljaju, m ožda uopšte nećete videti uzajam nu blokadu, iako je ona i dalje m o-
guća. N ula kao argum ent na kom andnoj liniji čini da program prilično brzo uđe u
uzajam nu blokadu.
O bratite pažnju na to da objektim a tipa Stapic nisu potrebni interni identifikatori;
njih identifikuje njihov položaj u nizu stapici. K onstruktor svakog objekta tipa Filozof
dobija referencu na levi i desni Stapic. Svaki Filozof (sem poslednjeg) u inicijalizaciji biva
smešten izm edu sledećeg para Stapica. Poslednji Filozof dobija nulti Stapic kao svoj de-
sni i tako se obišao krug oko stola - naime, poslednji Filozof sedi odm ah do prvog i obo-
jica dele nulti Stapic. Ukoliko sada svi Filozofi istovrem eno pokušaju da jedu, svaki od
njih će m orati da čeka dok susedni Filozof ne spusti svoj Stapic. Program će zato ući u
uzajam nu blokadu.
Ukoliko Filozofi više vrem ena provode u razmišljanju nego u jelu, onda je i verovat-
noća da će im zatrebati deljeni resursi (Stapici) m nogo m anja, i vi ćete biti uvereni da pro-
gram ne m ože ući u uzajam nu blokadu (ako za ponder zadate vrednost različitu od nule
ili veliki broj Filozofa), iako to nije istina. Ovaj prim er je zanimljiv upravo zato što pokazu-
je da program može naizgled da radi ispravno, a da u stvari krije uzajam nu blokadu.
982 Misliti na Javi

Da biste rešili problem , m orate shvatiti da uzajam na blokada nastaje ako su


istovrem eno ispunjena četiri uslova:
1. Uzajam na isključivost. Barem jedan resurs koji zadaci upotrebljavaju ne sme biti
deljiv. U ovom slučaju, svaki Stapic u svakom trenutku može 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 rečima, da bi se ostvarila uzajam na blokada, Filozof m ora
držati jedan Stapic i čekati na drugi.
3. Resurs ne m ože biti predu p red n o oduzet zadatku. Zadaci oslobadaju resurse
isključivo kao norm alan događaj. Naši Filozofi su pristojni i ne otim aju Stapice je-
dan od drugog.
4. Može se javiti kružno čekanje, p ri kojem prvi zadatak čeka na resurs koji koristi
drugi zadatak, drugi čeka na resurs koji koristi treći 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, kružno čekanje nastaje zato
što svaki Filozof najpre pokušava da uzm e Stapic s desne strane, a zatim onaj sleva.
Pošto svi ovi uslovi m oraju biti ispunjeni da bi se ostvariia uzajam na blokada, dovolj-
no je da sprečite sam o jedan od njih i izbegli ste uzajam nu blokadu. U ovom program u,
najlakši način sprečavanja uzajam ne blokade jeste rušenje četvrtog uslova. Taj uslov je
ispunjen zato što svaki F ilozof pokušava da uzm e svoje Stapice po određenom redosledu:
prvo desni, onda levi. Zato je m oguća situacija u kojoj svaki od njih drži svoj desni Stapic
i čeka da uzm e levi, a to je uslov za kružno čekanje. M eđutim , kada bi poslednji Filozof
bio inicijalizovan tako da prvo pokuša da uzm e levi štapić, a tek onda desni, tai Filozof ne
bi mogao da spreči Filozofa sa svoje desne strane da uzm e štapić izmedu njih. Time bi
kružno čekanje bilo sprečeno. To je sam o jedno rešenje problem a, a rešili biste ga i spreča-
vanjem ispunjenja bilo kojeg od preostalih uslova. (Više o tom e pročitajte u nekoj od
knjiga za naprednije višenitno program iranje):

//: paralelno/PopravljeniFi1ozofiKojiVeceraju.java
// Filozofi koji večeraju 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 izvršavanje 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
izvršavanje 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 pušta levi Sta-
pic pre desnog, i program će raditi dobro.
Java nem a podršku za sprećavanje uzajam ne blokade; m orate je sami izbeći tako što
ćete pažljivije projektovati program . To nisu utešne reči za onoga ko pokušava da otkrije
i otkloni grešku iz program a koji upada u uzajam nu blokadu.
Vežba 31: (8) Izmenite program UzajamnoBiokiraniFilozofiKojiVeceraju.java na sle-
deći način: kada filozof upotrebi štapiće, neka ih ne spušta na sto nego u zdelu. Kada fi-
lozof hoće da jede, uzim a prva dva dostupna štapića iz te zdele. Da li je tim e izbegnuta
m ogućnost uzajam ne blokade? Da li ćete prostim sm anjenjem broja dostupnih štapića
ponovo uvesti m ogućnost uzajam ne blokade?

Nove komponente biblioteke


U biblioteci java.util.concurrent u Javi SE5 postoji znatan broj novih klasa za rešavanje
problem a paralelnog izvršavanja. Ako naučite da ih koristite, to će vam pom oći da pišete
jednostavnije i robusnije program e za paralelno izvršavanje.
U ovom odeljku prikazaću reprezentativan uzorak prim era raznih kom ponenata, ali
nekoliko njih - one koje se ređe sreću i koriste —ovde neće biti razm otrene.
Pošto ove kom ponente služe za rešavanje raznih problem a, ne postoji jasan način da ih
organizujem o, pa ću krenuti od jednostavnijih prim era ka sve složenijim.

CountDovvnLatch - brava sa odbrojavanjem


Koristi se za sinhronizaciju jednog ili više zadataka tako što ih prim orava da čekaju na
dovršenje skupa operacija koje obavljaju drugi zadaci.
Najpre CountDovvnLatch objektu date početnu vrednost. Svaki zadatak koji za taj ob-
jekat pozove m etodu a w a it(), blokira se dok se ta vrednost ne izjednači 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 obično radi kada zadatak obavi svoj posao. CountDownLatch se ko-
risti jednokratno; vrednost se ne m ože 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 izjednači s nulom .
Ova klasa se obično koristi tako što se problem podeli na n nezavisno rešivih zadataka
i napravi objekat tipa CountDownLatch kojem se dodeli početna vrednost n. Svaki
završen zadatak poziva co u n tD o w n () za taj objekat. Zadaci koji čekaju da se problem reši
pozivaju a w ait() za taj objekat, da bi sačekali dok problem bude rešen. Tu tehniku prika-
zuje sledeći 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 naćin čekanja
}
}
public void radiStaTreba() throws InterruptedException {
TimeUnit.MILLISEC0NDS.sleep(slucajan.nextInt(2000));
print(this + "završen");
}
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 izvršavanje 985

}
public void run() {
try {
brava.await();
print(this + " prošao 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 završe
}
} /* (Pokrenite da biste videli rezultat) *///:-

DeoZadatka spava tokom nasum ično odabranog vrem enskog razdoblja i tako sim u-
lira izvršavanje dela zadatka, a ZadatakKojiCeka predstavlja deo sistema koji m ora da
sačeka dok se prvi deo problem a ne reši. Svi zadaci rade sa istim objektom tipa Count-
DownLatch definisanim u m etodi m a in ().
Vežba 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 pažnju na to da DeoZadatka sadrži statičan objekat tipa Random, što znači da
bi više zađataka moglo istovrem eno da pozove R andom .nextInt(). Da li je to bezbedno?
Ako problem postoji, u ovom slučaju ga možete rešiti tako što ćete objektu DeoZadat-
ka dati sopstveni Random objekat, tj. ukloniti m odifikator static. Ali ostaje opšte pitanje
o svim standardnim m etodam a iz Javine biblioteke: koje su bezbedne u višenitnom radu,
a koje nisu?
Nažalost, u dokum entaciji JDK ne postoji odgovor. Ispostavlja se da Random .nextInt()
jeste bezbedna u višenitnom izvršavanju, ali što se tiče drugih m etoda, moraćete to sami da
otkrivate od slučaja do slučaja, bilo pretraživanjem Weba bilo pregledanjem koda Java bi-
blioteke. To i nije baš pohvalno za programski jezik koji je, barem u teoriji, projektovan
upravo zato da podrži paralelno izvršavanje.
986 Misliti na Javi

CyclicBarrier
Klasu CyclicBarrier koristite u situacijam a kada hoćete da napravite gru p u zadataka koji
se izvršavaju paralelno, a zatim da sačekate dok se svi oni ne završe pre nego što pređete
na sledeći korak (nešto kao j o in ( ), recim o). Prilikom prolaska kroz barijeru svi paralelni
zadaci su završeni, pa napred m ožete 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 može koristiti više puta.
Simulacije m e fasciniraju od prvih susreta s računarom , a paralelno izvršavanje je
ključni faktor koji om ogućuje simulacije. Prvi program koji sam napisao22 bila je sim ula-
cija: igra s konjskim trakam a napisana u BASIC-u, nazvana (zbog ograničene dužine
im ena datoteka) HOSRAC.BAS. Sledi objektno orijentisana, višenitna 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 način izlaska
} catch(BrokenBarrierException e) {
// Želimo da dobijemo obaveštenje 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 izvršavanje 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 trkališta
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 ože biti priđ od ata „akcija barijere", a to je klasa koja rea-
lizuje interfejs Runnable i izvršava se autom atski kada se (na početku zadat) broj izjed-
nači s nulom - to je druga razlika izm eđu 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 odštam pa kada je stigao na cilj, ali se u tom
slučaju redosled prikazivanja m enja u zavisnosti od m ehanizm a za raspodelu procesor-
skog vrem ena nitim a (zadacim a). Klasa CydicBarrier om ogućuje svakom konju da radi
sve što treba kako bi došao do cilja, a zatim m ora da čeka na barijeri dok ne dođu 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 trkališta.
Kada svi zadaci prođ u barijeru, ona je autom atski sprem na za sledeću 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 neograničen BlockingQ ueue (blokirajući red za čekanje) objekata koji realizuju
interfejs Delayed, tj. svaki je odložen na neko vreme (engl. delayed). O bjekat može biti
uzet iz reda za čekanje tek kada njegovo vrem e odlaganja istekne. Red je tako uređen da
je čeonom objektu preostalo vrem e odlaganja koje se najviše skratilo od početka izvrša-
vanja. Ako se nijedno vrem e odlaganja nije skratilo, onda nem a čeonog elem enta i meto-
da p o ll( ) vraća null (zato u taj red za čekanje ne m ožete stavljati null elemente).
U sledećem 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 najviše skra-
tilo od početka izvršavanja) i izvršava 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 izvršavanje 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(okiđac > 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 tekućom niti


} catch(InterruptedException e) {
// Prihvatljiv način izlaska
}
print("Završen 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 nasumičnim odlaganjima:
for(int i = 0; i < 20; i++)
redzacekanje.put(new 0dlozeniZadatak(slucajan.nextInt(5000)));
// Zadaj tačku 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()
Završen PotrosacOdlozenihZadataka
* ///:-

OdlozeniZadatak sadrži listu List<OdlozeniZadatak> nazvanu sekvenca koja održa-


va redosled kojim su zadaci pravljeni, pa m ožem 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 ogućuje laku konverziju vrem enskih jedinica bez ikakvih
proračuna. 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
saopštim o u kojim jedinicam a jeste i u kojim želimo da bude, i to na ovaj način:

NAN0SEC0NDS.convert(delta, MILLISECONDS);

U metodi getD elay(), željena jedinica se prosleduje kao argum ent jedinica i pom oću
nje vreme proteklo od okidača 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 prosleđuje kao argum ent.)
Poglavlje 21: Paralelno izvršavanje 991

Za uređivanje, interfejs Delayed nasleđuje i interfejs Comparable, pa m oram o reali-


zovati m etodu com pareT o() tako da proizvodi razum na poređenja. M etode to S trin g () i
sazetak() form atiraju izlaz, a ugnežđena klasa StrazarNaCilju služi za gašenje 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 ože da izvršava svaki zadatak koji izađe iz reda za čekanje.
Pošto se zadaci izvršavaju redom prioriteta reda za čekanje, u ovom prim eru nem a po-
trebe da pravim o zasebne niti za izvršavanje odloženih zadataka (objekata tipa Odloze-
niZadatak).
Iz rezultata vidite da redosled pravljenja zadataka nem a uticaja na redosled izvršavanja,
nego se zadaci izvršavaju redosledom kojim im ističe odlaganje, kao što smo očekivali.

PriorityBlockingQueue
U suštini, ovo je p rio ritetn i red za čekanje čije se operacije vađenja 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 način 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); // Najniži 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() {
// Neograničen red za čekanje; nikada se ne blokira.
// Popuniču brzo nasumičnim prioritetima:
for(int i = 0; i < 20; i++) {
redzacekanje.add(new ZadatakSPrioritetom(slucajan.nextlnt(10)));
Thread.yield();
}
// Ubacujem postepeno poslove najvišeg prioriteta:
try {
for(int i = 0; i < 10; i++) {
TimeUni t.MILLISECONDS.sleep(250);
redzacekanje.add(new ZadatakSPrioritetom(lO));
}
// Dodajem poslove, najpre one s najnižim prioritetom:
Poglavlje 2 1: Paralelno izvršavanje 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 način izlaska
}
print("Završen 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 izvršavanje zadatka upotrebi tekuću nit:
rzc.take(),run();
} catch(InterruptedException e) {
// Prihvatljiv način izlaska
}
pri nt("Završen 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 poređenja sa stvarnim redosledom izvršavanja. M etoda
run( ) spava kratko, nasum ićno 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 eđusobno su
povezani preko objekta tipa PriorityBlockingQueue. Pošto blokirajući 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 pokušate da ga čitate, pošto će on
blokirati čitaoca kada m u ponestane elemenata.
99 4 Misliti na Javi

Kontroler staklenika napravljen pomoću


ScheduledExecutora
U poglavlju Unutrašnje klase predstavljen je prim er hipotetičkog staklenika i njegovog
upravljačkog sistema koji uključuje, isključuje i podešava oprem u. To se m ože posm atrati
kao jedna vrsta problem a paralelnog izvršavanja, pri kom e je svaki željeni đogađaj u stak-
leniku zadatak koji se izvršava u unapred definisano vreme. Klasa ScheduIedThread-
PoolExecutor im a sve što treba za rešavanje ovog problem a. M etodam a sch ed u le() (koja
zadatak izvršava jednom ) ili schedu!eAtFixedRate() (koja izvršavanje zadatka ponavlja u
pravilnim intervalim a) priprem ate objekte koji realizuju interfejs Runnable za izvrša-
vanje u određeno buduće vreme. U poredite naredni prim er i prim er iz poglavlja Unu-
trašnje 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 napišite kod za upravljanje
// hardverom koji fizički pali svetlo.
System.out.println("Uključujem svetlo");
svetlo = true;
}
Poglavlje 2 1: Paralelno izvršavanje 995

class IskljuciSvetlo implements Runnable {


public void run() {
// Ovde napišite kod za upravljanje hardverom koji fizički gasi svetlo.
System.out.println("Gasim svetlo");
svetlo = false;
}
}
class UkljuciVodu implements Runnable {
public void run() {
// Ovde napišite kod za upravljanje hardverom.
System.out.println("Uključujem vodu u stakleniku");
voda = true;
}
class IskljuciVodu implements Runnable { }
public void run() {
// Ovde napišite kod za upravljanje hardverom.
System.out.println("Isključujem vodu u stakleniku");
voda = false;
}
}
class TermostatNoc implements Runnable {
public void run() {
// Ovde napišite kod za upravljanje hardverom.
System.out.println("Termostat podešen za noć");
podesiTermostat("Noć");
}
}
class TermostatDan implements Runnable {
public void run() {
// Ovde napišite kod za upravljanje hardverom.
System.out.println("Termostat podešen 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,
// pošto je raspoređivač ugašen:
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) {
// Praviću se da je interval duži nego što zapravo jeste:
poslednjiPut.set(Calendar.MINUTE,
poslednjiPut.get(Calendar.MINUTE) + 30);
// Verovatnoća promene smera 1/5:
if(slucajan.nextlnt(5) == 4)
smerTemp = -smerTemp;
// Sačuvaj 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, inače bi sve
// RadneTačke imale reference na isti poslednjiPut.
// Za jednostavan objekat kao što je Calendar,
// metoda clone() je dovoljno dobra.
Poglavlje 21: Paralelno izvršavanje 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 vlažnosti u stakieniku. RadnaTacka sađrži i prikazuje jedan skup merenja, dok je
PrikupiPodatke planiran zadatak koji generiše sim ulirane podatke i dodaje ih listi
List<RadnaTacka> u Stakleniku nakon svakog svog izvršavanja.
O bratite pažnju na to da se m odifikatori volatile i synchronized upotrebljavaju na
odgovarajućim m estim a kako bi se sprečilo da zadaci sm etaju jedan drugom . Prilikoin
pravljenja liste koja sadrži RadneTačke, sve m etode bivaju sinhronizovane uslužnom me-
todom synchronizeđL ist() iz biblioteke java.util.Collections.
Vežba 33: (7) Izm enite RasporedjivacStaklenika.java tako da koristi DeIayQueue um e-
sto ScheduledExecutora.

Semafor
O bična brava (eksplicitna iz concurrent.locks ili ugrađena synchronized) u svakom tre-
nutku dozvoljava sam o jed no m zadatku da pristupi svakom resursu. Setnafor brojač om o-
gućuje da n zadataka istovrem eno pristupa jednom resursu. Možete sm atrati da semafor
izdaje dozvole za korišćenje odredenog resursa, m ada se zapravo ne upotrebljavaju nikak-
vi objekti dozvola.
Kao prim er, razm otrićem o pojam grupe objekata koja upravlja ograničenim brojem
objekata tako što dozvoljava da b ud u pojedinačno izdati na u p otrebu i p otom vraćeni
kada korisnik završi svoj posao s njima. Te funkcije ćem o kapsulirati u generičkoj klasi:

//: paralelno/Grupa.java
// Semafor u grupi objekata koji ograničava 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 tački
}
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 više nije potreban, prosledite ga m etodi primiObjekat-
NazadUGrupu().
Poglavlje 21: Paralelno izvršavanje 999

Niz izdat tipa boolean prati objekte koji su izdati na korišćenje, 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 više nem a dostup nih dozvola za korišćenje objekata iz grupe (što znači da u grupi
više nem a objekata). M etoda prim iObjekatNazadUGrupu() vraća dozvolu sem aforu
ukoliko je objekat koji se vraća validan.
Kao p rim er upotrebićem 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; // Spreči optimizaciju
private static int brojac = 0;
private final int id = brojac++;
public Debeli() {
// Skupa operacija koja se može 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 kupićem o ove objekte u grupu da bism o ograničili uticaj tog konstruktora. Klasu
Grupa ćem o testirati pravljenjem zadatka koji objekte tipa Debeli uzima na korišćenje,
drži ih neko vrem e i zatim vraća 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 +"vraćen " + stavka);


grupa.primiObjekatNazadUGrupu(stavka);
} catch(InterruptedException e) {
// Prihvatljiv način 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 korišćenje ");
f.operacija();
lista.add(f);
}
Future<?> blokiran = exec.submit(new Runnable() {
public void run() {
try {
// Semafor sprečava daljnje izdavanje,
// pa se poziv blokira:
grupa.izdajObjekatNaKoriscenje();
} catch(InterruptedException e) {
print("izdajObjekatNaKoriscenjef) prekinuta");
}
}
});
TimeUnit.SEC0NDS.sleep(2);
blokiran.cancel(true); // Izađi iz blokiranog poziva
print("Vraćanje objekata u " + lista);
for(Debeli f : lista)
grupa.primiObjekatNazadUGrupu(f);
for(Debeli f : 1 ista)
grupa.primiObjekatNazadUGrupu(f); // Drugi primiObjekatNazadUGrupu
// ignorišem
exec.shutdown();
}
} /* (Pokrenite da biste videli rezultat) * / / / : -
Poglavlje 2 1: Paralelno Izvršavanje 1001

U m etodi m a in (), pravi se Grupa objekata tipa D ebeli i skup ZadatakaKojiUzimaju-


Resurs koji počinje da je upotrebljava. Zatim nit m a in () počinje da uzim a objekte tipa
D ebeli i da ih ne vraća natrag. Kada uzme sve objekte iz grupe, semafor neće dozvoliti
dalje izdavanje objekata na korišćenje. Zato se blokira m etoda r u n () objekta blokiran, i
nakon dve sekunde poziva se m etoda ca n cel() da izađe iz tog objekta tipa Future. O bra-
tite pažnju na to da Grupa ignoriše redu n d an tn a vraćanja objekata.
U ovom prim eru računao sam na to da će se klijent Grupe uvek setiti da dobrovoljno
vrati objekte u grupu, što je najjednostavnije rešenje kada funkcioniše. Ukoliko ne može-
te da se oslonite na to, knjiga Thinking in Patterns (na adresi www.M indView.net) sadrži
dalja istraživanja načina upravljanja objektim a izdatim na korišćenje iz grupe.

Exchanger
Exchanger (razmenjivač) jeste barijera koja om ogućava da se m eđusobno zam ene objekti
dvaju zadataka. Kada zadaci uđu u barijeru, im aju svaki po jedan objekat, a kada izađu,
im aju objekat koji je prethodno im ao onaj drugi zadatak. Ovakvi razmenjivači se obično
koriste kada jedan zađatak pravi objekte čija je proizvodnja skupa, a drugi zadatak ih
troši; na taj način, može se napraviti više objekata koji se istovrem eno troše.
Da bism o upotrebili klasu Exchanger, napravićem o zadatke proizvođača i potrošača
koji preko generičkih 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 sadrži 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() završe
svoj posao, lista tipa List<T> biće 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 način 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 način izlaska.
}
System.out.println("Konačna 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 izvršavanje 1003

T imeUni t .SECONDS.sl eep(odl aganje);


exec.shutdownNow();
}
} /* Ispis: (primer)
Konačna 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 toleriše 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 trošenje druge m ogu da se odvijaju istovremeno.
Vežba 34: (1) Izm enite ExchangerPrimer.java tako da umesto klase Debeli upotrebite
neku svoju.

Simulacija
Jedna od najzanimljivijih i najuzbudljivijih prim ena paralelnog izvršavanja jeste pra-
vljenje simulacija. Zbog paralelnosti, svaka kom ponenta simulacije može biti zaseban
zadatak, a sim ulaciju je zato m nogo lakše 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 službenika


Ova klasična simulacija predstavlja svaku situaciju u kojoj se objekti pojavljuju nasu-
m ično i za čiju je obradu - koju obavlja ograničen broj poslužilaca, potrebna nasum ična
količina vrem ena. M ožem o napraviti sim ulaciju za izračunavanje idealnog broja
poslužilaca.
U ovom prim eru, potrebno je da se svakom klijentu banke posveti određeno vreme, a
to je broj vrem enskih jedinica koje službenik m ora da potroši na klijenta da bi ga uslužio.
Količina vrem ena za pružanje usluge razlikuje se za svakog klijenta i utvrdičem o je nasu-
mično. Sem toga, ne znam o koliko klijenata dolazi u svakom vrem enskom intervalu, pa
ćem o i to utvrditi nasum ično.

//: paralelno/SimulacijaSalterskogSluzbeni ka.java


// Pomoću redova za čekanje i višenitnog izvršavanja.
// {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 + "]";
}
}

// Nauči red klijenata da se prikaže:


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

// Nasumično 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 završava");
}

class Sluzbenik implements Runnable, Comparable<Sluzbenik> {


private static int brojac = 0;
private final int id = brojac++;
// Broj klijenata usluženih 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 izvršavanje 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 + "završava");
}
public synchronized void radiNestoDrugo() {
usluzenihKlijenata = 0;
redKlijenataSeUsluzuje = false;
}
public synchronized void usluzujRedKlijenata() {
assert !redKlijenataSeUsluzuje:"već uslužujem: " + this;
redKlijenataSeUsluzuje = true;
notifyAl1 ();
}
public String toString() { return "Službenik " + 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;
// Počni s jednim službenikom:
Sluzbenik sluzbenik = new Sluzbenik(klijenti);
1006 Misliti na Javi

exec.execute(sluzbeni k);
radiSluzbenika.add(sluzbenik);
}
public void prilagodiBrojSluzbenika() {
// Ovo je zapravo upravljački sistem. Menjanjem brojeva otkrićete
// nestabilne radne tačke upravljačkog mehanizma.
// Ako je red predugačak, dodaj još jednog službenika:
if(klijenti.size() / radiSluzbenika.size() > 2 ) {
// Ako su službenici na pauzi ili rade nešto drugo,
// vrati jednog nazad:
if(sluzbenikaKojiRadeNestoDrugo.size() > 0) {
Sluzbenik sluzbenik = sluzbenikaKojiRadeNestoDrugo.remove();
sluzbenik.usluzujRedKl ijenata();
radiSluzbenika.offer(sluzbenik);
return;
}
// U protivnom napravi (zaposli) novog službenika
Sluzbenik sluzbenik = new Sluzbenik(klijenti);
exec.execute(sluzbenik);
radiSluzbenika.add(sluzbenik);
return;
}
// Ako je red dovoljno kratak, ukloni jednog službenika:
if(radiSluzbenika.size() > 1 &&
klijenti.size() / radiSluzbenika.size() < 2)
premestiJednogSluzbeni ka();
// Ako reda uopšte nema, treba nam samo jedan službenik:
if (kl ijenti .size() == 0)
whi1e(radiS1uzbenika.size() > 1)
premestiJednogSluzbenika();
}
// Daj službeniku drugi posao ili ga pošalji 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 izvršavanje 1007

System.out.println(this + "završava")-
}
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 predugačak, klijenti će otići:
RedKlijenata klijenti =
new RedKl ijenata(NAJVECI_D0ZV0LJENI_RED);
exec.execute(new GeneratorKlijenata(klijenti));
// Rukovodilac dodaje i uklanja službenike 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
izvršavanje 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 }
Službenik 2 prekinut
Službenik 2 završava
Službenik 1 prekinut
Službenik 1 završava
RukovodilacSluzbenika prekinut
Rukovodi1acSluzbenika završava
Službenik 3 prekinut
Službenik 3 završava
Službenik 0 prekinut
Službenik 0 završava
GeneratorKlijenata prekinut
GeneratorKlijenata završava

O bjekti tipa Klijent veoma su jednostavni, pošto sađrže sam o jedno final int polje.
Pošto se ti objekti nikada ne menjaju, oni su samo za čitanje i ne zahtevaju sinhronizaciju
niti m odifikator volatile. Sein toga, svaki zadatak Službenik u svakom tren u tk u uklanja
1008 Misliti na Javi

sam o po jedan objekat tipa Klijent iz ulaznog reda i uslužuje ga dok ne završi, 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 kaže, čine klijenti koji čekaju da ih usluži neki objekat tipa
Službenik. 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 ičnim
vrem enskim intervalim a dodaje klijente u red.
Službenik uzim a klijente iz objekta tipa RedKIijenata i obrađuje ih jednog po jednog,
pam teći broj klijenata koje je uslužio tok om određene sm ene. Može m u se poručiti da
radiN estoD rugo() kada nem a dovoljno klijenata i usluzujRedKlijenata( ) kada dođe
m nogo klijenata. Službenika koji će biti vraćen na opsluživanje ulaznog reda bira m etoda
com pareTo() koja poredi broj usluženih klijenata da bi p rio ritetn i red za čekanje (Prio-
rityQueue) m ogao autom atski da stavi najm anje opterećenog službenika napred.
RukovodilacSluzbenika upravlja svim aktivnostima. On prati sve službcnike i nadgleda
šta rade klijenti. Jedna od zanimljivosti ove simulacije jeste pokušaj otkrivanja optim alnog
broja službenika za dati dotok klijenata. To možete videti u metodi prilagodiBrojSluzbe-
n ik a() koja je upravljački sistem za dodavanje i uklanjanje službenika na stabilan način. Svi
upravljačld sistemi m oraju da paze na stabilnost; ako na prom enu reaguju prebrzo, izgubiće
stabilnost, a ako reaguju presporo, sistem će preći u jedan od ekstrema (red klijenata
maksimalne dužine, a ne rade svi dostupni službenici ili su zaposleni svi službenici iako kli-
jenata nem a).
Vežba 35: (8) Izm enite program SimuIacijaSalterskogSluzbenika.java tako da predsta-
vlja Web klijente koji šalju zahteve o dređenom broju servera. Treba utvrditi opterećenje
koje ta grupa servera može da savlada.

Simulacija restorana
Jednostavan prim er Restoran.java prikazan u prethodnom delu poglavlja obogatiću u
ovoj simulaciji dodavanjem više kom ponenata kao što su objekti tipa Narudzba i
DeoObroka; pored toga, ponovo ću upotrebiti klase jelovnik iz poglavlja Nnbrojani tipovi.
Predstaviću i Java SE5 klasu SynchronousQueue što je blokirajući 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 - uspećete jedino u slučaju da ta osoba pruži 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 može biti posluženo sam o jedno jelo.
Ostale klase i funkcije u ovom prim eru slede iz strukture program a R estoran.java ili
predstavljaju prilično 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 izvršavanje 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 vraća 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 može 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 posluži:
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 ograničenu veličinu:
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 poslužuje 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 izvršavanje 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 narudžbu:
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()) {
// Došao 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 izvršavanje
programa");
System.in.read();
}
exec.shutdownNow();
}
} /* Ispis: (primer)
Konobar 0 primio PROLECNE_ROLNICE što poslužuje gostu Gost 1
Gost 1 jede PROLECNE_ROLNICE
Konobar 3 primio PROLECNE_ROLNICE što poslužuje gostu Gost 0
Gost 0 jede PROLECNE_ROLNICE
Konobar 0 primio PLJESKAVICU što poslužuje gostu Gost 1
Gost 1 jede PLJESKAVICU
Konobar 3 primio PR0LECNE_R0LNICE što poslužuje gostu Gost 2
Gost 2 jede PR0LECNE_R0LNICE
Konobar 1 primio SUPU što poslužuje gostu Gost 3
Gost 3 jede SUPU
Konobar 3 primio L0VACKI_PIR što poslužuje gostu Gost 0
Gost 0 jede LOVACKI_PIR
Konobar 0 primio VOCE što poslužuje gostu Gost 1

*///:-
Poglavlje 21: Paralelno izvršavanje 1013

Jedna od veom a važnih stvari na koje u ovom prim eru treba obratiti pažnju jeste upra-
vljanje složenošću tako što su za kom unikaciju izm eđu zadataka upotrebljeni redovi za
čekanje. Već sama ta tehnika uveliko pojednostavljuje postupak paralelnog program iranja
tako što invertuje kontrolu: zadaci se ne obraćaju jedan drugom neposredno, nego jedni
drugim a šalju objekte preko redova za čekanje. Zadatak koji je prim io objekat obrađuje ga
i tretira ga kao poruku, um esto da ga podvrgava poruci. Ukoliko se budete pridržavali tog
načina rada, imaćete m nogo više šanse da pravite robusne paralelne sisteme.
Vežba 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 više G ostiju za stolom.

Raspodela posla
Naredni prim er je sim ulacija koja obuhvata više koncepata iz ovog poglavlja. Zamislite
robotizovanu m ontažnu liniju za autom obile. Svaka Kola se proizvode u nekoliko faza,
počev o d pravljenja šasije, preko m ontaže m otora, pogonskog sistema i točkova.

//: paralelno/ProizvodnjaKola.java
// Složen 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 isključeno");
}
}

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 završe
// Stavi kola u redZaZavrsnu za dalju obradu
redZaZavrsnu.put(kola);
}
} catch(InterruptedException e) {
print("Izlazim iz niti Monter pomoću prekida");
} catch(BrokenBarrierException e) {
// Želim da dobijem obaveštenje o ovome
throw new RuntimeException(e);
}
Poglavlje 21: Paralelno izvršavanje 1015

print("Monter isključen");
}

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 pomoću prekida");
}
print("Izvestilac isključen");
}

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() različit 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 završen...
i skljuci Se ();
}
} catch(InterruptedException e) {
print ("Izl azim iz niti " + this + " pomoću prekida");
} catch(BrokenBarrierException e) {
// Želim da dobijem obaveštenje o ovome
throw new RuntimeException(e);
}
1016 Misliti na Javi

print(this + " isključen");


}
private synchronized void
iskljuciSe() throws InterruptedException {
angazuj = false;
monter = nul1; // Otkači od objekta Monter
// Vraćamo se u grupu dostupnih:
grupa.release(this);
while(angazuj == false) // Isključi 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 Točkove");
monter.kolaO .dodajTockove();
}
}

class GrupaRobota {
// (Nečujno) sprečava identične 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 izvršavanje 1017

grupa.remove(r);
r.dodeliMontera(d);
r.angazuj(); // Ukljući ga da obavi posao
return;
}
wait(); // Nema dostupnih
unajmi(tipRobota, d); // Rekurzivno pokušaj 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 oću objekta tipa RedKola, što je tip reda Lin-
kedBlockingQueue. PravljenjeSasije pravi kostur Kola i sm ešta ga u RedKola. Monter
skida Kola i/. RedaKola i unajm ljuje Robote da na njim a rade. Barijera tipa CyclicBarrier
om ogućuje 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 sledeću operaciju. Potrošač završnog objekta
tipa RedKola jeste Izvestilac koji sam o ispisuje sastav Kola da bi pokazao da su svi zadaci
pravilno izvršeni.
Robotima se upravlja u grupi, i kada nešto treba da se uradi, odgovarajući Robot biva
unajmljen iz grupe. Nakon dovršetka posla, Robot se vraća u grupu.
U m etodi m a in () prave se svi potrebni objekti i inicijalizuju svi zadaci; poslednje se
pokreće PravljenjeSasije da bi se pokrenuo proces. (M eđutim , ponašanje reda Linked-
BIockingQueue je takvo da sam m ogao njega prvog da pokrenem .) O bratite pažnju 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 gašenja bezbedan.
Uočili ste da su sve m etode Kola sinhronizovane. Ispostavlja se da je to u ovom primeru
redundantno, pošto su Kola unutar proizvodnje uvek u nekom od redova za čekanje i na
svakim kolima u svakom trenutku može raditi sam o jedan zadatak. U suštini, redovi za če-
kanje nam eću serijsko pristupanje objektima tipa Kola. Aii upravo to i jeste zamka - mogli
1018 Misliti na Javi

biste reći: „Hajde da optimizujemo perform anse tako što klasu Kola nećem o sinhronizovati,
pošto 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 komentariše:
M nogo je lakše reći„Kola m ogu biti upotrebljena iz više niti, pa hajde da ih napravim o
očigledno bezbednim za višenitno izvršavanje". Evo kako ja to posm atram : na strm im
m estim a u parkovim a postoje zaštitne ograde i natpisi „Z abranjeno naslanjanje na
zaštitnu ogradu“. Naravno, prava svrha ovog pravila nije da spreči naslanjanje na ogradu,
nego da spreči padanje niza strm inu. Ali je m nogo lakše pridržavati se pravila „Zabranje-
no naslanjanje na ogradu“ nego pravila „Zabranjeno padanje niza strminu".
Vežba 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.
Vežba 38: (3) Koristeći pristup iz program a ProizvodnjaKola.java, m odelujte priču o
gradnji kuće koja je data u ovom poglavlju.

Optimizacija performansi
Z natan broj klasa u biblioteci Jave SE5 java.util.concurrent postoji radi poboljšanja
perform ansi. Tokom prelistavanja biblioteke concurrent, teško ćete razlučiti klase na-
m enjene za redovnu upotrebu (kao što su redovi BlockingQueue) od klasa koje su sam o
za poboljšavanje perform ansi. U ovom odeljku razm otrićem o neka od tih pitanja i klasa
za optim izaciju performansi.

Poređenje tehnologija uzajamno isključivih brava (mutexaj


Pošto Java sada obuhvata i staru rezervisanu reč synchronized i nove klase Lock i Atomic
Jave SE5, zanimljivo je uporediti te različite pristupe da bism o bolje shvatili vrednost sva-
kog od njih i saznali gde ih treba upotrebljavati.
Bilo bi previše naivno napraviti po jedan jednostavan test za svaki od tih pristupa, kao
u sledećem prim eru:

//: paralelno/JednostavnoMikropoređenjePerforinansi -java


// Opasnosti od mikropoređenja 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 izvršavanje 1019

public void povecajZaJedan() {


brava.lock();
try {
++brojac;
} finally {
brava.unlock();
}
}
}

public class JednostavnoMikropoređenjePerformansi {


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 korišćenje poziva m etode sinhronizovane pom oću rezervisane


reči sy n chron ized naizgled mnogo brže od upotrebe objekta tipa Lock. Šta se tu desilo?
Ovaj prim er pokazuje opasnosti od tzv. m ikropoređenja perform ansi.23 Taj pojam po
pravilu upućuje na izolovano m erenje perform ansi, izvan konteksta. N aravno, ni tvrdnje
kao što je „Klasa Lock je m nogo brža od rezervisane reči synchronized“ ne m ožete izri-
cati bez p rethodnog merenja. Ali, kada pišete takva ispitivanja, m orate biti svesni toga šta
se događa tokom prevodenja i u vreme izvršavanja.
G ornji prim er je problem atičan iz nekoliko razloga. Prvo i osnovno, stvarnu razliku u
p erform ansam a videćemo samo u slučaju da za uzajam no isključive brave (mutexe)
postoji takmičenje, dakle više zadataka m ora pokušavati da pristupi delovim a koda
zaštićenim mutexima. U gornjem prim eru, svaki m utex ispitujem o pom oću sam o jedne
niti u m etodi m a in ( ), izolovano.
U rugo, m ožda prevodilac obavlja posebne optim izacije kada ugleda rezervisanu reć
synchronized, a možda 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 . Više info rm acija o m eren ju p erfo rm an si pročitajte
u njegovom ćlanku na ađresi w w w -t28.ibm .com /devdoperw orks/librarY /j-jtpl2214.
1020 Misliti na Javi

da shvati kako se brojac povećava fiksan broj p u ta pa da unapred izračuna rezultat. Ima
različitih prevodilaca i sistema za izvršavanje, pa je teško reći šta će se tačno dogoditi, ali
m oram o sprečiti m ogućnost da prevodilac predvidi ishod.
D a bi testiranje bilo validno, program m ora biti složeniji. Prvo m oram o napraviti više
zadataka, i to ne sam o onih koji m enjaju interne vrednosti, već i onih koji te vrednosti či-
taju (inače b i optim izator m ogao prepoznati da se te vrednosti nikada ne koriste). Pored
toga, izračunavanje m ora biti toliko složeno i nepredvidljivo da prevodilac nem a šanse da
obavi agresivnu optim izađju. To ćem o postići prethodnim učitavanjem velikog niza
pseudoslučajnih celih brojeva (p reth od no učitavanje sm anjuje uticaj poziva m etode
R andom .nextInt() na glavne petlje) i sabiranjem tih brojeva:

//: para'le'lno/PoredjenjeSinhronizacija.java
// Poređenje performansi pri upotrebi eksplicitnih objekata tipa Lock
// i Atomic i performansi pri korišćenju rezervisane reči 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 = “greška";
protected final static int VELICINA = 100000;
protected static int[] unapredUcitani = new int[VELICINA];
static {
// Učitaj niz slučajnih 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 izvršavanje 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 steći
// 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 izvršavanje 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 početno testiranje ne obuhvata troškove
// prvog pokretanja niti.
// Napravi više mernih tačaka:
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 izvršavanje 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


zajednički 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()
izražavaju različite načine realizacije uzajamnog isključivanja.
U prethodnom programu, zadaci se izvršavaju pom oču objekta tipa FbcedThreadPool
u pokušaju da se sve niti naprave na početku i kako bi se sprečili eventualni dodatni tro-
škovi tokom testiranja. Za svaki slučaj, početno testiranje se ponavlja i prvi rezultati od-
bacuju, zato što obuhvataju i početno pravljenje niti.
CyclicBarrier je potrebna zato što obezbeđuje da se svi zadaci završe pre nego što sva-
ko testiranje proglasimo završenim.
Ođredba static je upotrebljena za prethodno učitavanje niza slučajnih brojeva, pre
početka testiranja. Na taj način se tokom testiranja ne vide režijski troškovi generisanja
slučajnih brojeva.
Metoda ak u m u liraj() nakon svakog poziva pomera se na sledeće mesto u nizu una-
predUcitani (kada dođe do kraja, vraća se na njegov početak) i vrednosti broj dodaje još
jedan slučajno generisan broj. Više zadataka Modifikator i Citalac nadmeće se za dobi-
janje objekta Akumulator.
O bratite pažnju na to da sam u TestuKlaseAtomic napomenuo kako je situacija pre-
više složena da bismo upotrebili objekte tipa Atomic - u suštini, ukoliko ima više Atomic
obiekata, morate da odustanete i koristite konvencionalnije uzajamno isključive brave
(m utexe). (U dokumentaciji JDK izrićito pišeda je korišćenje objekata tipa Atomic dobro
samo kada je kritično ažuriranje objekta svedeno na jednu promenljivu.) Međutim, test
sam ipak ostavio da biste stekli uvid u poboljšanje performansi koje prouzrokuju Atomic
objekti.

Videti Ihinking in l'attcnis na ađresi www.MindView.net.


1026 Misliti na Javi

U metodi m a in (), testiranje se ponavlja i možete odlučiti da zatražite više od pet (po-
drazumevanih) ponavljanja. U svakom ponavljanju, broj ciklusa testiranja se udvo-
stručuje, pa možete videti kako se razne uzajamno isključive brave (m utexi) ponašaju
kada se izvršavaju sve duže i duže. Kao što vidite iz izlaza, rezultati su prilično iznenađu-
ju ći. U prve četiri iteracije izgleda da je rezervisana reč synchronized efikasnija od klasa
Lock i Atomic. Ali iznenada neka granica biva prekoračena pa deluje da synchronized
postaje veoma neefikasna, dok Lock i Atom ic kao da približno održavaju svoju srazmeru
prema testu OsnZaPor (osnova za poređenje) i stoga postaju mnogo efikasniji od syn-
chronized.
Im ajte u vidu da ovaj program daje tek naznaku razlike između raznih realizacija uza-
jam no isključivih brava i da gornji izlaz pokazuje te razlike samo na m om konkretnom
računaru pod m ojim konkretnim okolnostima. Kao što ćete videti kada budete eksperi-
mentisali s ponašanjem, ono se znatno menja u zavisnosti od broja niti i trajanja izvrša-
vanja programa. Neke optimizacije vrućih tačaka pozivaju se tek nekoliko minuta nakon
početka izvršavanja programa, a u slučaju serverskih programa, nakon nekoliko sati.
Uza sve to, prilično je jasno da je klasa Lock najčešće znatno efikasnija od rezervisane
reči synchronized, a izgleda i da se režijski troškovi upotrebe rezervisane reči synchroni-
zed znatno meniaju, dok oni klase Lock ostaju relativno ujednačeni.
Da li to znači da rezervisanu reč synchronizeđ uopšte ne treba upotrebljavati? U obzir
morate uzeti dva činioca: prvo, u primeru PoredjenjeSinhronizacija.java tela mutex
metoda su izuzetno mala. Uopšte uzev, to je dobro - uzajamno isključite samo one delove
programa koje apsolutno morate. Međutim, uzajamno isključeni delovi u praksi mogu
biti veći nego u gornjem primeru, te će i procentualni deo vremena koji se provodi u telu
metoda verovatno biti znatno veći nego režijski troškovi od ulaska i izlaska iz uzajamno
isključive brave, a to bi moglo da poništi eventualno poboljšanje koje prouzrokuje
ubrzanje uzajamno isključive brave. Naravno, istinu ćete saznati tek kada isprobate
različite pristupe i vidite njihov uticaj, a to bi trebalo da radite tek prilikom optimizacije
performansi.
Drugo, ko pročita kod u ovom poglavlju videće da rezervisana reč synchronized
proizvodi mnogo čitljiviji kod od idioma zaključaj-try/finally-otključaj, za koji su po-
trebne brave tipa Lock, i zato sam u ovom poglavlju uglavnom koristio synchronized. Na
više mesta u ovoj knjizi rekao sam da se kod mnogo češće čita nego piše — u programi-
ranju je važnije da kod razumeju drugi ljudi nego računar - te je čitljivost koda ključna.
Zato ima smisla početi s rezervisanom reči synchronized i preći na objekte tipa Lock tek
prilikom optim izacije performansi.
Najzad, lepo je kada možete da upotrebite klase Atomic u paralelnom programu, ali
morate voditi računa o tome da su, kao što smo videli u programu PoređenjeSinhroni-
zacija.java, Atomic objekti korisni samo u veoma jednostavnim slučajevima, 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 pokušati prelazak na Atoinic objekte kasnije, ukoliko to budete morali da učinite zbog
performansi.
Poglavlje 2 1: Paralelno izvršavanje 1027

Kontejneri bez zaključavanja


U poglavlju Čuvanje objekata naglasio sam da su kontejneri osnovna alatka za celo pro-
gram iranje, a to važi i za paralelno programiranje. Zato su prvi Java kontejneri (kao Vec-
tor i Hashtable) imali mnogo sinhronizovanih metoda koje su prouzrokovale
neprihvatljive režijske troškove kada nisu bile korišćene u višenitnim programima. U Javi
1.2 nova kontejnerska biblioteka bila je desinhronizovana, a klasa Collections je dobila
razne statičke ,,sinhronizovane“ dekorativne metode za sinhronizaciju različitih tipova
kontejnera. Iako je to bilo poboljšanje jer je programer mogao da bira da li da koristi
sinhronizaciju kontejnera, režijski troškovi su i dalje zavisili od zaključavanja pom oču re-
zervisane reči synchronized. U Javu SE5 dodati su novi kontejneri baš da bi se poboljšale
performanse bezbednog višenitnog rada, a za to su upotrebljene pametne tehnike kojim a
se izbegava zaključavanje.
Opšta strategija u pozadini tih kontejnera bez zaključavanja jeste sledeća: modifikacija
kontejnera je dozvoljena istovremeno kada i čitanje njihovog sadržaja, ukoliko čitaoci
mogu da vide samo rezultate završenih modifikacija. Modifikacija se obavlja na zasebnoj
kopiji određenog 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 završena 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 završi, 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 više iteratora, pa ne morate da pišete
specijalni kod za zaštitu od takvih izuzetaka, kao što ste morali pre.
Klasa CopyOnWriteArraySet upotrebljava CopyOnW riteArrayList da bi svoje po-
našanje postigla bez zaključavanja.
Klase ConcurrentHashM ap i ConcurrentLinkedQueue koriste slične tehnike da bi
om ogućile paralelno čitanje i upisivanje, ali kopiraju i modifikuju samo delove kontej-
nera, a ne ceo kontejner. Međutim, čitaoci ne vide modifikacije pre nego što budu završe-
ne. ConcurrentHashM ap ne baca izuzetke ConcurrentM odificationException.

O performansama
Ukoliko iz kontejnera bez zaključavanja uglavnom čitate, biće to mnogo brže nego da či-
tate iz njegove sinhronizovane verzije, zato što izbegavate režijske troškove od zaključa-
vanja i otključavanja. To važi i za mali broj upisivanja u kontejner bez zaključavanja, ali bi
bilo zanimljivo steći neku predstavu koliko je to ,,malo“. U ovom odeljku daćemo 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

Počeću od generičke strukture za testiranje svih tipova kontejnera, uključujući tu i


Mape. Generički 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 izvršavanje 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() vraća inicijalizovan kontejner koji tre-
ba testirati i smešta ga u polje kontejnerZaTestiranje. Druga apstraktna metoda,
pokreniCitaoceIU pisivace(), pokreće zadatke čitanja i upisivanja koji će čitati i modifi-
kovati testirani kontejner. Razni testovi se obavljaju s različitini brojem čitalaca i upi-
sivača da bi se video uticaj takm ičenja za bravu (za sinhronizovane kontejnere)
i upisivanja (za kontejnere bez zaključavanja).
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 zađaci završili), iniđjalizuje 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 sačuvao 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 odgovarajuće klase Citalac i Upisivac:

//: paralelno/PoredjenjaListi .java


// {Args: 1 10 10} (Brza verifikaciona provera tokom builda)
// Grubo poređenje performansi lista bezbednih u višenitnom 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 izvršavanje 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 određene radnje u listi


List<Integer>. U metodi Citalac.upisiR ezultate() skladišti se trajanje ali i rezultat,
k a k o bi se sprečilo da optim izacija izbaci izračunavanja. Zatim se definiše 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 sprečili inicijalizatorK ontejnera() da napravi i inicijalizuje konkretne kontejnere
za testiranje.
1032 Misliti na Javi

U metodi m a in () vidite varijacije testiranja s različitim brojevima čitalaca i upisivača.


Zbog poziva Tester.inicM M ain(args), promenljive testiranja možete 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 „vrućih tačaka“ i
sakupljanje smeća.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 približno iste performanse bez obzira na broj čitalaca i upisivača - čitaoci se nad-
meću za brave sa drugim čitaocima, isto kao upisivači. Međutim, CopyOnW rite-
ArrayList je mnogo brži kada nema upisivača i još uvek znatno brži s pet upisivača. Iz-
gleda kao da CopyOnW riteArrayList možete bezbrižno da koristite koliko god hoćete;
troškovi od upisivanja u listu kao da neko vreme ostaju manji nego troškovi od sinhroni-
zacije ceie liste. Naravno, oba pristupa m orate isprobati u konkretnoj aplikaciji da biste
pouzdano utvrđili koji je bolji.
Podsećam da ovo ni približno nije dobro poređenje performansi za dobijanje apsolut-
nih brojeva. Vi ćete gotovo sigurno dobiti drugačije brojeve. Cilj mi je bio samo da ste-
knete uvid u relativna ponašanja te dve vrste kontejnera.
Pošto skup CopyOnW riteArraySet upotrebljava listu CopyOnW riteArrayList, nje-
govo ponašanje će biti slično i ovde ga ne moramo zasebno ispitivati.

Poređenje realizacija Mapa


Istu strukturu možemo 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 poređenje performansi mapa bezbednih u višenitnom 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 izvršavanje 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 upisivača na ConcurrentHashM ap manji je čak i od uticaja na Co-


pyOnW riteArrayList, ali ConcurrentHashM ap upotrebljava drugačiju tehniku koja
očigleđno minimizuje uticaj (troškove) upisivanja.

Optimističko zaključavanje
Pored toga što objekti tipa Atomic izvršavaju atomske operacije kao što je decrement-
AndGet( ), neke Atomic klase omogućuju i tzv. optimističko zaključavanje. To znači da
prilikom izračunavanja ne koristite uzajamno isključivanje, ali da nakon završetka izraču-
navanja za ažuriranje Atomic objekta koristite metodu com pareA ndSet(). Prosleđujete
jo j staru i novu vrednost, i ako se stara vrednost ne podudara s vrednošću pronađenom u
Atomic objektu, operacija se završava neuspehom - to znači da je neki drugi zadatak u
međuvremenu izmenio objekat. Ne zaboravite da inače uvek upotrebljavamo mutex (re-
zervisanu reč synchronized ili objekat tipa Lock) i tako sprečavamo da više zadataka isto-
vremeno modifikuje objekat, ali ovde se ponašamo optimistički tako što podatke
ostavljamo nezaključane i nadamo se da drugi zađaci neće uleteti i modifikovati ih. Narav-
no, sve to radimo samo zbog performansi - korišćenjem objekta tipa Atomic umesto re-
zervisane reči synchronized ili objekta tipa Lock, verovatno ćete poboljšati performanse.
Šta se dešava ako operacija com pareA ndSet() zakaže? Stvari postaju čupave, pa ovu
tehniku možete da prim enjujete samo na projekte koje možete krojiti kako god potrebe
nalažu. Ako com pareA ndSet() zakaže, morate da odlučite š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 isključivanje (m utexa). Možda možete po-
kušati da ponovite operaciju i neće smetati ako ona (tek) drugi put uspe. Ili možda može-
te da zanemarite neuspeh - u nekim simulacijama, gubitak jedne tačke ne znači ništa za
celinu, jer je broj tačaka ogroman. (Naravno, model morate poznavati toliko dobro da
znate je li prethodna hipoteza istinita.)
Poglav[je 2 !: Paralelno izvršavanje 1035

Zamislimo fiktivnu simulaciju koja se sastoji od 100 000 ,,gena“ dužine 30; recimo da je
to početak nekakvog genetičkog algoritma. Pretpostavimo da je za svaku ,,evoluciju“ gene-
tičkog algoritma potreban veoma skup proračun, pa ste odlučili da upotrebite višeproce-
sorski računar, rasporedite zadatke među procesorima i tako poboljšate performanse. Sem
toga, umesto objekata tipa Lock koristite Atomic objekte da biste izbegli režijske troškove
uzajamnog isključivanja. (Naravno, program ste najpre napisali na najjednostavniji mo-
gući način, pomoću rezervisane reči synchronized. Tek kada je takav program proradio,
otkrili ste da je prespor i počeli da primenjujete tehnike za poboljšavanje performansi!)
Zbog prirode ovog modela, zadatak koji otkrije sudar tokom proručuna, može da ga zane-
mari i da ne ažurira 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()) {
// Nasumično 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 proračun 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 poboljšati performanse. (Tu
pretpostavku ćemo testirati u jed noj od vežbi.) Svaki objekat tipa Evolver uprosečava
svoju vrednost s prethodnom i sa sledećom, i ako ažuriranje ne uspe, on ispisuje tu vred-
nost i ide dalje. Imajte u vidu da u programu nema uzajamnog isključivanja.
Vežba 39: (6) Da li su pretpostavke u programu BrzaSimulacija.java razumne? Izmenite
niz tako da sadrži proste cele brojeve umesto objekata tipa Atom iclnteger i upotrebite
Lock brave za uzajamno isključivanje (mutexe). Uporedite performanse te dve verzije
programa.

ReadWriteLock
ReadVVriteLock optimizuje situaciju kada u strukturu podataka upisujete relativno ret-
ko, ali iz nje više zadataka često čita. Klasa ReadVVriteLock om ogućuje da do prvog
pokušaja upisivanja istovremeno radi više čitalaca. Nakon zaključavanja brave za pisanje,
čitaoci ne mogu da rade sve dok se brava za pisanje ponovo ne otključa.
Nemoguće je unapred reći da li će ReadVVriteLock poboljšati performanse vašeg pro-
grama; to zavisi od činilaca kao što su srazmera broja čitanja i broja modifikovanja poda-
taka, trajanje operacije čitanja i upisivanja (brava je složenija, pa kratke operacije ne daju
da se vidi poboljšanje), količina takm ičenja među nitima i da li se program izvršava na
višeprocesorskom računaru. Na kraju krajeva, jedini način da saznate da li ReadWri-
teLock poboljšava 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 pošten:
private ReentrantReadWriteLock brava =
Poglavlje 21: Paralelno izvršavanje 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 više čitalaca može zaključati
// (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 način 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 način 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 može sadržati fiksan broj objekata proizvoljnog tipa. Nje-


nom konstruktoru morate dati željenu veličinu liste i početnu vrednost kojom listu treba
popuniti. Metoda s e t() zaključava bravu za upisivanje kako bi mogla da pozove pripadnu
metodu A rrayL ist.set(), a metoda g e t() zaključava bravu za čitanje kako bi mogla da po-
zove metodu A rrayList.get(). Pored toga, g e t() proverava da li je više čitalaca pribavilo
(zaključalo) bravu za čitanje i ako jeste, prikazuje njihov broj da bi dokazala kako više či-
talaca može tu bravu istovremeno da zaključa.
Kao test klase ListaCitalacalUpisivaca, TestListeCitalacalUpisivaca pravi zadatke za
čitaoce i upisivače u objekat tipa ListaCitalacaIUpisivaca<Integer>. Vodite računa o
tome da je broj upisivanja mnogo manji od broja čitanja.
Pročitajte dokumentaciju JDK za klasu ReentrantReadVVriteLock i videćete da ona
ima više drugih metoda i da se spom inju nekakva ,,ravnopravnost“ i „strateške odluke“ Ta
alatka je prilično sofisticirana, pa je treba koristiti samo kada pokušavate da poboljšate
performanse. U prvoj verziji programa treba da koristite jednostavnu sinhronizaciju, a
ReadVVriteLock primenjujte samo ako morate
Vežba 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
pomoću prilagodenog programa PoredjenjaMapa.java. Kakve su u odnosu na
performanse sinhronizovanog objekta tipa HashMap odnosno ConcurrentHashM ap?
Poglavlje 21: Paralelno izvršavanje 1039

Aktivni objekti
Kada proučite ovo poglavlje, možda ćete steći utisak da je u Javi veoma složeno i teško ko-
rektno sprovesti višenitni rad. Pored toga, izgleda i pomalo kontraproduktivno - iako
zadaci rade paralelno, morate uložiti mnogo truda da realizujete tehnike za sprečavanje
tih zadataka kako ne bi smetali jedan drugom.
Ako ste ikada pisali u asembleru, pisanje višenitnih programa izaziva isti osećaj: važna
je svaka sitnica, sve morate sami da uradite i nema zaštite u vidu provere koju sprovodi
prevodilac.
Da problem nije u samom modelu višenitnog rada? Na kraju krajeva, on je preuzet re-
Iativno nepromenjen iz sveta proceduralnog programiranja. Možda postoji drugačiji mo-
del paralelnog rada, prikladniji za objektno orijentisano programiranje.
Jedan od alternativnih pristupa nazvan je aktivni objekti ili aktori.26 Objekti su proglaše-
ni ,,aktivnim“ zato što svaki objekat održava sopstvenu radnu nit i red za poruke; svi za-
htevi koji se odnose na taj objekat ulaze u njegov red za čekanje i izvršavaju se jedan po
jedan. Dakle, kod aktivnih objekata serijalizujemoporuke, a ne tnetode, što znači da više ne
moramo da se štitimo od problema koji nastaju kada se zadatak prekine usred petlje.
Kada aktivnom objektu pošaljete poruku, ona se transformiše u zadatak koji ulazi u ob-
jektov red za čekanje na kasnije izvršavanje. Za realizaciju te šeme podesna je klasa Future
Jave SE5. Evo jeđnostavnog primera u kojem dve metode stavljaju pozive u red za čekanje:

//: paralelno/PrimerAktivnogObjekta.java
// Može 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);
// Ubaciću odlaganje nasumičnog trajanja da bih postigao efekat
// trajanja proračuna:
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 našao vremena da m i to objasni.


1040 Misliti na Javi

public Integer c a l l () {
print("pokrećem " + 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("pokrećem " + 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 ();
// Sprečava 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("Završeni 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)
Završeni pozivi svih asinhronih metoda
pokr eć em 0.0 + 0.0
pokrećem 0.2 + 0.2
0.0
pokrećem 0.4 + 0.4
0.4
pokrećem 0.6 + 0.6
0.8
Poglavlje 2 1: Paralelno izvršavanje 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
*///:-

„Izvršilac jedne niti“ proizveden pozivom metode Executors.newSingleThread-


E x e c u to r() održava sopstveni neograničeni blokirajući red za čekanje i ima samo jednu
nit koja vadi zadatke iz reda i izvršava ih do završetka. 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 unutrašnjoj klasi. O bratite pažnju na to
da sve metode aktivnih objekata imaju povratnu vrednost tipa Future, s generičkim pa-
ram etrom koji je stvarni povratni tip te metode. Na taj način se poziv metode gotovo tre-
nutno vraća, a pozivalac pomoću tog objekta tipa Future saznaje kada je zadatak završen
i dobija stvarnu povratnu vrednost. Time je rešen najsloženiji slučaj, 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 vraćaju 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 završi 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 sprečio slučajni međusobni uticaj niti, argumenti prosleđeni 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 teško sprovesti,
pošto Java to zasad ne podržava.)
Za aktivne objekte važi sledeće:
1 . Svaki objekat ima sopstvenu radnu nit.
2. Svaki objekat zadržava potpunu kontrolu nad svim svojim poljima (što je nešto
strože nego kod običnih 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 privlačni. Pošto poruka jednog aktivnog objekta drugom može biti
blokirana samo ako se odloži njen ulazak u red za čekanje, i pošto je to odlaganje uvek
veoma kratko i ne zavisi od drugih objekata, slanje poruke se zapravo ne može blokirati
1042 Misliti na Javi

(najgore što se može desiti je kratko odlaganje). Pošto sistem aktivnih objekata kom uni-
cira samo preko poruka, dva objekta ne mogu biti blokirana dok se nadmeću da pozovu
metodu nekog trećeg objekta, a to znači da ne može nastati uzajamna blokada, što je veliki
korak napred. Pošto radna nit aktivnog objekta u svakom trenutku izvršava samo po jed-
nu poruku, nema takm ičenja za resurse i ne moram o da sinhronizujemo metode. Sinhro-
nizađja i dalje postoji, ali na nivou poruka, zato što se pozivi metoda ubacuju u red za
čekanje, pa se u svakom trenutku može izvršavati samo jedan od njih.
Nažalost, prevodilac ne daje neposrednu podršku za aktivne objekte, a ručno pro-
gramiranje po gornjem obrascu previše je zametno. Međutim, 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 raču-
nara niti mreže. Ne bi me iznenadilo da agentski orijentisano programiranje nasledi
objektno orijentisano program iranje, zato što se u njemu kombinuju objekti i relativno
lako rešenje za paralelno izvršavanje.
Više inform acija o aktivnim objektim a, aktorima i agentima možete naći na Webu;
neke ideje realizovane u aktivnim objektim a možete naći naročito u C.A.R. Hoareovoj
teoriji komunicirajućih sekvencijalnih procesa (Communicating Seciuential Processes, CSP).
Vežba 41: (6) Programu PrimerAktivnogObjekta.java dodajte deo za obradu poruka
koji nema povratnu vrednost, a poziva se iz metode m a in ().
Vežba 42: (7) Izmenite VoskOMatik.java tako da realizuje aktivne objekte.
Projekat:27 Pomoću anotacija i Javassista napravite anotaciju klase @Active koja odre-
dišnu klasu transformiše u aktivan objekat.

Sažetak
Trebalo je da iz ovog poglavlja naučite osnove paralelnog programiranja pomoću Javinih
niti, kako biste shvatili sledeće:
1. Možete izvršavati više nezavisnih zadataka.
2 . Morate uzeti u obzir sve moguće probleme prilikom gašenja tih zadataka.
3 . Zadaci se međusobno om etaju prilikom korišćenja deljenih resursa. Osnovna alat-
ka za sprečavanje takvih sudara je uzajamno isključiva brava (mutex).
4 . Ako zadatke ne projektujete pažljivo, oni se mogu uzajamno blokirati.
Neophodno je naučiti kada se koristi paralelno izvršavanje, a lcada da ga izbegavati.
Osnovni razlozi za upotrebu jesu:
• Da biste posao podelili na više zadataka i tako efikasnije koristili računar. Time se
postiže i (za programera nevidljiva) raspodela zadataka na više 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 rešenjim a ne sadrži
rešenja projckata.
Poglavjje 21: Paralelno izvršavanje 1043

Klasičan primer uravnoteženog korišćenja resursa jeste upotreba procesora dok se


čeka na završetak ulazno/izlaznih operacija. Bolja organizacija koda obično se postiže u
sim ulacijama. Klasičan primer pogodnosti za korisnika jeste nadziranje dugmeta za pre-
kid tokom dugačkih preuzimanja s mreže.
Dodatna prednost niti je to što ,,teška“ prebacivanja iz konteksta jednog procesnog
okruženja u kontekst drugog (reda veličine nekoliko hiljada naredaba) zamenjuju ,,la-
kim“ prebacivanjem iz konteksta jednog izvršnog okruženja u kontekst drugog u okviru
istog procesnog okruženja (reda veličine nekoliko stotina naredaba). Pošto sve niti jednog
procesa dele isti m em orijski prostor, pri lakom prebacivanju iz konteksta zamenjuju se
samo promenljive izvršavanja programa i lokalne promenljive. S druge strane, promena
procesa - dakle, teška promena konteksta - mora da zameni ceo memorijski prostor.
Glavni nedostaci višenitnog izvršavanja su:
T. Usporenje rada dok niti čekaju na deljene resurse.
2. Za upravljanje nitima troši se dodatno procesorsko vreme.
3. U slučaju lošeg projektovanja, programi postaju neopravdano složeni.
4 . Postoji verovatnoća da će biti patoloških pojava kao što su nemogućnost dobijanja
resursa, trka za resursima, uzajamna blokada i uzajamna blokada uprkos izvrša-
vanju (više niti izvršava pojedinačne zadatke koje sve zajedno ne mogu da završe).
5. Uslovi za paralelno izvršavanje menjaju se u zavisnosti od platforme. (Pišući pri-
mere za ovu knjigu, otkrio sam uslove za trku koji su se brzo pokazali na nekim
računarima, a uopšte se nisu pojavili na drugim.) Ukoliko program budete razvija-
li na jednom od računara na kojem se Ioši delovi ne vide, doživećete neprijatno iz-
nenađenje kada taj program distribuirate.
Jedna od najvećih poteškoća pri višenitnom izvršavanju nastaje zato što više zadataka
deli (istovrerneno koristi) neki resurs - kao što je m em orija objekta - a programer mora
sprećiti da više zadataka istovremeno pokuša da čita i menja taj resurs. To se može postići
prom išljenom upotrebom dostupnih mehanizama za zaključavanje (npr. rezervisane reči
synchronized). Te alatke jesu neophodne, ali ih morate dobro upoznati pošto će inače
ćutke proizvesti uzajamno blokiranje.
Pored toga, za primenu niti je potrebna izvesna umešnost. Java omogućuje pravljenje
proizvoljnog broja objekata potrebnog za rešavanje problema - barem u teoriji. (Na
primer, pravljenje miliona objekata za inženjersku analizu metodom konačnih elemenata
u Javi nije izvodljivo bez projektnog obrasca Flyweight.) Međutim, izgleda da postoji gor-
nja granica broja niti koje se mogu napraviti, pošto od neke granice niti postaju zametne.
Tu kritičnu granicu nije lako otkriti. Ona se često menja u zavisnosti od operativnog
sistema i JV M -a; može biti manja od sto, a i veća od nekoliko hiljada. Pošto se za rešenje
problema često pravi tek nekoliko niti, ovo najčešće i nije neko ograničenje, ali u opštijem
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 višenitno programiranje izgleda u određenom
jeziku ili biblioteci, smatrajte ga nekom vrstom magije. Uvek vas nešto može ujesti kada
se najm anje nadate. Problem večere filozofa zanimljiv je zato što se može napisati tako da
se retko javlja uzajamna blokada i zato vam daje utisak da sve lepo radi.
1044 Misliti na Javi

Višenitni rad bi po pravilu trebalo primenjivati pažljivo i štedljivo. Ukoliko problemi


s višenitnim radom postanu veliki i složeni, možda bi trebalo da ih rešite na jeziku kao što
je Erlang. To je jedan od nekoliko funkcijskih jezika specijalizovanih za višenitno programi-
ranje. Na takvom jeziku treba napisati samo one delove programa u kojima je neophodno
višenitno izvršavanje - ako ih ima mnogo i ako su dovoljno komplikovani da opravdavaju
takav pristup.

Literatura za dalje usavršavanje


Nažalost, o paralelnom programiranju postoji mnogo netačnih informacija - to pokazuje
koliko je ono samo po sebi zamršeno i koliko je lako pomisliti da ste ga najzad shvatili. (Go-
vorim iz vlastitog iskustva, pošto sam već više puta pomišljao kako sam ga napokon savla-
dao; i ne sumnjam da me u budućnosti čekaju neprijatna otkrića.) Kada uzmete u ruke bilo
kakav nov dokument o paralelnom radu, uvek se morate zapitati koliko njegov autor za-
pravo zna o tome što piše. 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 suštini, ovo je ,,who’s
who“ u svetu višenitnog izvršavanja 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 pretočio 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
više jezika i tehnologija. lako može biti teška, zaslužuje da je pročitate više puta (najbolje
na po nekoliko meseci, kako biste stigli da usvojite pročitano). Doug je jedan od retkih
ljudi koji zaista razumeju paralelnost, pa će vam se trud isplatiti.
T he Jav a Language Specificatioti (treće izdanje), poglavlje 17, autori Gosling, Joy,
Steele i Bracha (Addison-Wesley, 2005). Tehnička specifikacija dostupna je i u obliku
elektronskog dokumenta, na adresi: http://java.su n .com /docs/books/jls.
The Thinking in Java Annotated Solu-
Rešenja o d ab ran ih vežbi data su u elek tron sk om d o k u m en tu
tion Guide, koji se m ože kupiti na lokaciji www.MindView.com.
Grafička korisnička okruženja
Jedan od osnovnih priticipa projektovanja glasi: „neka što je jednostavno postane lako, a ono
štoje teško postane mogućeV

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 omogući da napravi
grafičke 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 grafičko okruženje podjednako osrednjeg izgleda na svim si-
stemima. Osim toga, ta biblioteka je jako ograničena: dozvoljeno je korišćenje samo četiri
fonta, a ne može se pristupiti nijednom naprednijem elementu grafičkog okruženja 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 smišljen, projek-
tovan i realizovan za mesec dana. To je sigurno čudo produktivnosti, ali i lekcija o tome
zašto je projektovanje važno.
Situađja se poboljšala nakon uvođenja modela AWT iz Jave 1.1 koji koristi nmogo ja-
sniji, objektno orijentisan pristup, a podržava i zrna Jave, tj. model programiranja kom -
ponenata usmeren ka lakom pravljenju vizuelnih razvojnih okruženja. U Javi 2 (JD K 1.2)
dovršena je transformacija starog AWT-a iz Jave 1.0 tako što je sve zamenjeno Javinim
osnovnitn klasama (engl. Java Foundation Classes, JFC) čiji se grafički deo zove Swing. To
je bogat skup Javinih zrna koja se lako koriste i razumeju, a pom oću njih se u vizuelnim
razvojnim alatima (prevlačenjem i otpuštanjem , kao i ručnim program iranjem ) može
napraviti grafičko okruženje kojim možete da budete zadovoljni. Izgleda da pravilo „treće
prepravke" koje važi u industriji softvera (proizvod nije dobar sve dok se triput ne pre-
pravi) važi i za programske jezike.
Ovo poglavlje posvećeno je isključivo modernoj biblioteci Swing iz Jave 2, pri čemu se
opravdano pretpostavilo da se grafička okruženja u Javi pišu pomoću Swinga.2Ako iz ne-
kog razloga morate da koristite prvobitni, stari AWT (radi podrške starom kodu ili usled
ograničenja koja nameće čitač), uvod u tu problematiku možete da pronađete u prvom
izdanju ove knjige na adresi www.MindView.net. O bratitc pažnju na to da Java i dalje
sađrži neke AWT komponente i da ih u nekim situacijama morate koristiti.
Imajte u vidu da ovo nije sveobuhvatan pregled kom ponenata grafičke biblioteke
Swing, niti svih metoda opisanih klasa. Grafička biblioteka Swing je ogromna, a iz ovog
poglavlja samo treba da naučite osnovne pojmove i da upoznate principe projekta. Uko-
liko vam treba više od toga, biblioteka Swing verovatno može da pruži ono što želite ako
ste voljni da je istražujete.
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 iznenađenja“, što znači da korisnika ne bi trebalo iznena-
đivati.
IBM j e za svoj program za uređivanje 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ć možete potražiti i na Webu, ali najbolje je da krenete od Sunovog udžbenika
za Swing na adresi http://java.sun.com/docs/books/tutorial/uiswing.
Postoji veliki broj (prilično debelih) knjiga posvećenih isključivo biblioteđ Swing i
njih ćete sigurno proučavati ako vam zatreba šire znanje, ili ako poželite da promenite
podrazumevano ponašanje biblioteke Swing.
Kako budete savladavali biblioteku Swing, otkrivaćete sledeće:
1. Biblioteka Swing je mnogo bolji model za programiranje od onih koje ste verovatno
sretali u drugim jezicim a i razvojnim okruženjima. Osnovna struktura za tu bibli-
oteku jesu zrna Jave (koja će biti objašnjena pri kraju ovog poglavlja).
2. Alatke za pravljenje grafićkih okruženja (vizuelna okruženja za programiranje)
obavezan su vid kompletnog razvojnog okruženja za Javu. Zrna Jave i Swing om o-
gućuju razvojnom okruženju da piše kod umesto vas, dok vi razmeštate kompo-
nente na obrasce pomoću grafičkih alatki. To u velikoj meri ubrzava projektovanje
grafičkog okruženja i om ogućuje slobodnije eksperimentisanje i isprobavanje di-
zajna, što posledično dovodi do izbora boljeg okruženja.
3. Jednostavnost i dobar projekat grafičke biblioteke Swing znači da će čak i ako kori-
stite vizuelnu alatku, umesto da ručno pišete kod, dobijeni kod biti razumljiv. To
rešava veliki problem s korišćenjem ranijih vizuelnih alatki kojima se često generi-
sao nerazumljiv kod.
Grafička biblioteka Swing sadrži sve komponente koje se očekuju u modernom korisnič-
kom okruženju, od dugmadi sa slikaina, preko stabala do tabela. To je velika biblioteka, ali
složenost odgovara zadatku koji se obavlja: ako je nešto jednostavno, ne morate da pišete
mnogo koda, ali ukoliko pokušate da uradite nešto teže, i kod postaje srazmerno složeniji.
Dobar deo privlačnosti biblioteke Swing potiče od nečega što bi se moglo nazvati ,,or-
togonalnost korišćenja". Odnosno, kada jednom steknete opšte ideje o ovoj biblioteci,
m oći ćete da ih prim enjujete svuda. Dok sam pisao primere iz ovog poglavlja, najčešće
sam već na osnovu imena metode mogao da zaključim šta ona radi, i to zahvaljujući stan-
dardu za imenovanje. To je, sasvim sigurno, obeležje dobre biblioteke. Pored toga, kom-
ponente se obično mogu dodavati drugim komponentama i sve će raditi ispravno.
Navigacija pom oću tastature je automatska: aplikaciju napisanu pomoću Svvinga ino-
žete da pokrenete i bez miša, što ne zahteva nikakvo dodatno programiranje. Podrška za
pom eranje sadržaja je izvanredna - treba samo da om otate komponentu u objekat klase
JScrollPane dok je dodajete u obrazac. Za dodavanje komponenata poput priručnih sa-
veta (engl. tooltips) obično je dovoljan samo jedan red koda.
Cela biblioteka Swing napisana je na Javi radi prenosivosti.
Swing podržava i prilično radikalnu mogućnost nazvanu „prilagodljiv izgled i ponaša-
nje“ (engl. pluggable look & feel), što znači da izgled grafičkog okruženja može dinamički
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. Moguće je (mada teško) napraviti čak i sopstveni izgled okru-
ženja. Neka takva okruženja možete naći i na Webu. '

M eni se najviše sviđa izgled i ponašanje Salveta (engl. nnpkin) autora Kena A rnolda, u kojem prozori
izgledaju kao da su skicirani na salveti. Posetite http://mipkinlaJ.sourceforge.iiet.
Poglavlje 22: Grafička korisnička okruženja 1047

Uprkos svim svojim pozitivnim aspektima, Swing nije za svakoga, niti je sve probleme
s korisničkim okruženjima rešio onako kako su njihovi projektanti zamislili. Na kraju
poglavlja razmotrićemo 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 okruženja 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 izvršavao u čitaču Weba (unutar tzv. bazena s pe-
skom, da ne bi naškodio klijentskom računaru). Predviđalo se da će Java aplet postati sle-
deća 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 piše aplete.
Iz raznih razloga, ta revolucija se nije odigrala. Dobar deo problema činilo je to što
većina računara nema potreban Java softver za izvršavanje apleta, a većina korisnika ne
želi da preuzme i instalira paket od 10 M B da bi prikazali nešto što su slučajno pokupili
s Weba. Već i spominjanje nečeg sličnog dovoljno je da uplaši mnoge korisnike. Java apleti
nikada nisu dostigli kritičnu masu kao sistem za distribuciju aplikacija klijentima, i
premda se apleti i đalje povremeno sreću, po pravilu pripadaju prošlosti računarstva.
To ne znači da apleti nisu zanimljiva i vredna tehnologija. Ukoliko možete 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 savršeni za đistri-
buciju klijentskih programa i automatsko ažuriranje svih računara i pri tom bi se izbegli
troškovi i trud koji obično prate instalaciju i distribuciju novog softvera.
Uvod u tehnologiju apleta naći ćete u mrežnim dodacima ove knjige, na adresi
www.MindView.net.

Osnove Svvinga
Većina Svving aplikacija, tj. grafičkih korisničkih okruženja, 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 pomoću 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 gašenja. Konstanta EXIT_O N_CLO SE saopštava mu da izađe iz
programa. Podrazumevano se u tom slučaju ne radi ništa, pa ako ne pozovete metodu
setD efaultC loseO peration() ili ne napišete ekvivalentan kod za vaš prozor, aplikađja se
neće završiti.
setS ize() zadaje veličinu prozora u pikselima.
O bratite pažnju na poslednji red:

p r oz or .s et Vi si bl e( tru e);

Bez njega, na ekranu ne biste videli ništa.


U prazan prozor (objekat tipa JFram e) dodaćemo 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 () piše neposredno u
GUI komponente. Swing ima sopstvenu nit za prim anje UI dogadaja i ažuriranje ekrana.
Ako sadržaj ekrana počnete da menjate pomoću drugih niti, možete izazvati sudare i uza-
jam nu blokadu opisane u poglavlju Paralelno izvršavanje.
Umesto toga, druge niti - kao prethodno spomenuta nit m a in () - treba Svvingovoj niti
za otpremu događaja (engl. event dispatch thread) da šalju (engl. submit) zadatke na
izvršavanje.4 To se postiže predajom zadatka metodi SwingU tilities.invokeLater( ) koja
zadatak smešta u redza čekanje dogadaja (engl. event queue), iz koga ih nit za otpremu do-
gađaja (u nekom trenutku) vadi i izvršava. 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 događaja pripada biblioteci AWT.


Poglavlje 22: Grafička korisnička okruženja 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 više ne rukujemo neposredno, nego šaljemo objekat koji realizuje in-
terfejs Runnable, a stvarno rukovanje obavlja nit za otpremu događaja kada u ređu za če-
kanje događaja dođe do tog zadatka. Kada bude izvršavala taj zadatak, ona neće raditi
ništa drugo, pa ne može nastati sudar - ukohko se sav kod u vašem programu bude prid-
ržavao tog pristupa, tj. rukovao ekranom pomoću 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 događaja.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 podržava. To ne znači da su njihovi
autori bili neznalice, nego da se preporučena 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 pažnju na to da poziv metode sleep () rtije unutar konstruktora. Ako ga sta-
vite tarno, prvobitni tekst natpisa („Jedan natpis“) uopšte neće biti prikazan, pošto u tom
slučaju konstruktor neće završiti dok se ne završi metoda sleep () i novi natpis ne bude
umetnut. Ali ako je poziv metode slee p () unutar konstruktora ili bilo koje UI operacije,
to znači da tokom spavanja koje prouzrokuje metoda sle e p () zadržavate izvršavanje niti
za otpremu događaja, što je po pravilu loše.
Vežba 1: (1) Izmenite ZdravoSwing.java tako da dokažete da se aplikacija ne gasi bez po-
ziva metode setD efauItCIoseO peration().
Vežba 2: (2) Izmenite ZdravoNatpis.java tako da pokažete da je dođavanje natpisa di-
namičko - stavite u prozor pseudoslučajan broj natpisa.

Alatka za prikazivanje
Objedinićem o navedene ideje i smanjiti količinu redundantnog koda sledećom 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: Grafička korisnička okruženja 1051

Ovu alatku ćete možda poželeti da koristite na više mesta, pa je smeštena 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). Statična metoda r u n () zadaje naslov prozora jednak Class
imenu objekta tipa JFrame.
Vežba 3: (3) Izmenite SlanjeSwingPrograma.java tako da upotrebljava program Swing-
Konzola.

PravIjenje dugmeta
Pravljenje dugmeta je prilično jednostavno: samo pozovite konstruktor klase JButton i
prosledite mu natpis koji treba da se pojavi na dugmetu. Kasnije ćete videti da možete
uraditi i nešto naprednije, npr. staviti slike na dugmad.
Referenca na dugme se obično čuva unutar klase da bi kasnije moglo ponovo da jo j se
pristupi.
JButton je Swing kom ponenta sa sopstvenim prozorčićem koja se automatski iscrtava
tokom ažuriranja prikaza. To znači da dugme ili neki drugi kontrolni objekat ne crtate
ručno, već ih samo postavljate na obrazac i om ogućujete im da se automatski iscrtavaju.
Dugme se na obrazac postavlja obično 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 nešto novo: pre nego što se element postavi u okno sadržaja, dodeljuje
mu se nov„raspoređivač“ 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 obično koristi rasporedivač tipa BorderLayout, ali to nam u ovom slučaju
ne odgovara zato što se na taj način (kao što ćete saznati u nastavku poglavlja) svaki kon-
trolni objekat potpuno prekriva novim objektima u istoj oblasti. Međutim, raspoređivač
1052 Misliti na Javi

tipa FlowLayout prouzrokuje ravnomerno ređanje kontrolnih objekata po obrascu, sleva


udesno i odozgo naniže.
Vežba 4: (1) Pokažite da se bez poziva metode setL ay o u t() u programu D ugm el.java
prikazuje samo jedno dugme.

Hvatanje događaja
Ako prevedete i izvršite prethodni program, primetićete da se neće dešavati ništa kada
pritiskate dugmad. U tu svrhu morate sami da napišete kod. Suština programiranja upra-
vljanog događajima, što čini veliki deo programiranja grafičkih korisničkih okruženja, je-
ste povezivanje događaja s kodom koji odgovara na njih.
Ovo se u grafičkoj biblioteci Swing postiže jasnim razdvajanjem okruženja (grafičkih
kom ponenata) i realizacije (koda koji želite da izvršavate kada se desi događaj). Svaka
komponenta može da prijavljuje događaje, i svaki od njih može da prijavljuje zasebno.
Dakle, ako niste zainteresovani, na primer, za to da li je pokazivač miša prešao preko dug-
meta, nećete osluškivati taj događaj. To je veoma jednostavan i elegantan način za pisanje
programa vođenih događajima. Kada shvatite osnovne pojmove, lako ćete koristiti Swing
komponente koje nikada niste videli. Zapravo, ovaj model se prim enjuje na sve što se
može okarakterisati kao zrno Jave (o čemu ćete saznati više u nastavku poglavlja).
Za početak, usredsredićemo se na glavne đogađaje kom ponenata. Za dugme tipa JBut-
ton, „glavni događaj“ je njegovo pritiskanje. Da biste objavili svoje zanim anje za dogadaj
pritiskanja dugmeta, pozivate metodu addActionListener( ) klase JButton. Ta metoda
očekuje argument koji realizuje interfejs ActionListener što sadrži 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 pozivaće se kad god se priti-
sne dugme (to se obično naziva povratni poziv , engl. callback).
Šta bi trebalo da bude rezultat pritiskanja tog dugmeta? Želeli bismo da vidimo da se
nešto menja na ekranu, pa će biti predstavljena nova Swing komponenta: JTextFieId. To
je jednoredno polje za unos teksta, ili, u ovom slučaju, polje u kome program može me-
njati tekst. Iako postoji više načina da se napravi objekat klase JTextFieId, najjednostav-
niji način je da se samo saopšti konstruktoru koliko treba da bude široko to polje. Kada se
polje za tekst postavi na obrazac, njegov sadržaj možete da menjate metodom setT ext()
(klasa JTextFieldsadrži i mnoge druge metode, ali njih morate da potražite sami u HTM L
dokumentaciji na adresi http://java.sun.com). Evo kako to izgleđa:

//: 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: Grafička korisnička okruženja 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.adđAc 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);
}
! ///:-

Jeđnoredno polje za tekst pravi se i smešta na obrazac na isti način 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 sadrži sve inform acije o događaju i mestu gde je na-
stao dogadaj. U ovom slučaju, želeo sam da opišem dugme koje je pritisnuto: metoda
g etS ou rce() vrača objekat gde se đesio događaj, a ja sam (eksplicitnom konverzijom) uči-
nio da njegov tip bude JButton. Metoda g etT ext() vraća 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 događaja oba dugmeta.
Ćesto je zgodnije da interfejs ActionListener bude realizovan kao anonimna unutra-
šnja klasa, naročito zato što se obično koristi samo jedna instanca svake klase ovog tipa.
Program DugmeŽ.java može se promeniti tako da koristi anonim nu unutrašnju klasu na
sledeći način:

//: gui/Duame2b.java
// Korišćenje anonimnih unutrašnjih 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 unutrašnje klase (kad god
je to moguće).
Vežba 5: (4) Napišite aplikaciju uz korišćenje kiase SvvingKonzola. Neka sadrži polje za
tekst i tri dugmeta. Kada se pritisne neko dugme, u polju treba da se pojavi nekakav tekst.

Višeredna polja za tekst


Klasa JTextArea slična je klasi JTextField, ali može da sadrži više redova teksta i da ima više
funkcija. Naročito korisna metoda je appen d (); pomoću nje možete lako da smestite izlaz
programa u polje za tekst. Pošto unutar višerednog polja za tekst možete da se krećete i una-
zad, Swing program je poboljšanje u poređenju sa onim što je dosad moglo da se postigne
pomoću programa s komandne linije koji ispisuju rezultate na standardnom izlaznom ure-
daju. Kao primer, razmotrimo sledeći program koji popunjava višeredno polje za tekst iz-
laznim podacima generatora Countries iz poglavlja Detaljno razmatranje kontejnera:

//: gui/TextArea.java
// Korišćenje 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: Grafićka korisnička okruženja 1055

public class TextArea extends JFrame {


private JButton
b = new JButton("Dodaj podatke"),
c = new JButton("Obriši 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 pažnju na to da se prijemnik tipa ActionListener za oba dugme-
ta pravi i dodaje bez definisanja posredne promenljive, pošto se on ne poziva nigde više u
programu. Dugme „Dodaj podatke“ formatira i dodaje sve podatke, dok dugme „Obriši
podatke“ koristi metodu setT ext() za uklanjanje sadržaja 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 sadržaj kada se na ekranu pojavi previše tek-
sta. To je sve što treba da uradite da biste potpuno omogućili pomeranje sadržaja na
ekranu. Pošto sam probao to da uradim i pom oću nekih drugih alata za programiranje
grafičkih korisničkih okruženja, bio sam oduševljen jednostavnošću i dobrim dizajnom
kom ponenata kao što je JScrollPane.
Vežba 6 : (7 1 P r e t v o r it e znakovninizovi/TestReguIarExpression.java u interaktivan
Svving program k o ji omogućuje umetanje ulaznog niza znakova u jedno višeredno
tekstualno polje i regularnog izraza u jedno jednoredno tekstualno polje. Rezultate (pri-
mene regularnog izraza na umetnuti znakovni niz) prikažite u drugom višerednom
tekstualnom polju.
1056 Misliti na Javi

Vežba 7: (5) Napravite aplikaciju pom oću klase SwingKonzoIa i dodajte sve Swing kom-
ponente koje imaju metodu ad dA ctionListener(). (Potražite ih u HTM L dokumentaciji
na adresi http://java.siin.com. Savet: upotebite indeks.) Uhvatite njihove događaje i za
svaki prikažite odgovarajuću poruku u polju za tekst.
Vežba 8: (6) Gotovo sve kom ponente biblioteke Swing izvedene su iz klase Component
koja ima metodu se tC u rso r(). Potražite je u H TM L dokumentaciji za Javu. Napišite ap-
likaciju i promenite pokazivač miša u neki iz klase Cursor.

Raspoređivanje elemenata
Način na koji se komponente raspoređuju u Javi verovatno je drugačiji nego u svim osta-
lim sistemima za grafička okruženja koje ste koristili. Prvo, sve se obavlja programski: ne
postoje ,,resursi“ koji upravljaju postavljanjem komponenata. Drugo, načinom na koji se
komponente postavljaju na obrazac ne upravlja apsolutno pozicioniranje, već raspoređivač
koji odlučuje o položaju komponenata na osnovu redosleda kojim ih dodajete. Veličina, ob-
lik i položaj komponenata znatno se razlikuju od jednog raspoređivača do drugog. Pored
toga, raspoređivači se prilagodavaju dimenzijama apleta ili prozora aplikacije, pa ako se
promeni dimenzija prozora, u saglasnosti s tim mogu da se promene veličina, oblik i po-
ložaj komponenata.
Klase JApplet, JFram e, JWindow, JDialog JPanel itd. mogu da sadrže i prikazuju
komponente. Klasa Container sadrži metodu setLayout( ) koja omogućuje zadavanje
rasporedivača. U ovom odeljku proučićem o raziičite raspoređivače tako što ćemo posta-
vljati dugmad (jer je to najjednostavnije). Nećemo obrađivati nikakve događaje, pošto ovi
primeri samo pokazuju kako se kom ponente raspoređuju.

Raspoređivač BorderLayout
Prozor podrazumevano koristi raspoređivač tipa BorderLayout. Ako ne zadate drugačije,
on uzima sve objekte koje mu dodajete i smešta ih u centar, i pri tom ih proširuje sve do
ivica.
Ovaj raspoređivač koristi četiri granične oblasti i centralnu oblast. Kada na pano koji
koristi raspoređivač BorderLayout dodate nešto, možete da upotrebite preklopljenu me-
todu a d d () čiji je prvi argument konstantna vrednost. Ta vrednost može da bude nešto od
sledećeg:

Borderl_ayout.NORTH fvrh)
BorderLayout. SOUTH |dno)
BorderLayout. EAST (desno)
BorderLayout.WEST (ievoj
BorderLayout.CENTER Ipopunjavanje sredine, do ostaiih komponenata ili ivica)
Poglavlje 22: Grafička korisnička okruženja 1057

Ako ne zadate oblast za postavljanje objekta, podrazumevano se koristi CENTER. Evo


jednostavnog primera. Raspoređivač se ne zadaje izričito jer JFram e podrazumevano ko-
risti raspoređivač tipa BorderLayout:

//: gu i/ Bo rderLayoutl.java
// Prikazuje raspoređivač 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, sažima se da bi se uklopio
u najm anji moguči prostor duž jedne dimenzije, a maksimalno se produžava duž druge
dimenzije. Oblast CENTER, međutim, proširuje objekat u obe dimenzije da bi zauzeo
sredinu.

Raspoređivač FlowLayout
Ovaj raspoređivač samo ,,reda“ komponente na obrazac, sleva udesno, sve dok se ne po-
puni jedan red, a zatim prelazi u sledeći red i nastavlja redanje.
Evo primera koji bira raspoređivač tipa FlowLayout i zatim postavlja dugmad na
obrazac. Prim etićete da u ovakvom načinu raspoređivanja komponente zadržavaju svoju
,,prirodnu“ veličinu. Dugme je, na primer, taman toliko da se može prikazati znakovni niz
koji sadrži.

//: g u i / F l o w L a y o u t l .java
// Prikazuje raspoređivač 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--
Raspoređivač tipa FlowLayout potpuno sm anjuje sve komponente, pa su rezultati po-
malo iznenađujući. Na primer, pošto će natpis tipa JLabel biti veličine znakovnog niza
koji sadrži, pokušaj da se tekst poravna udesno nema efekta ako se koristi raspoređivač
FlowLayout.
Vodite računa o tome da će raspoređivač prerasporediti komponente ukoliko prome-
nite veličinu prozora.

Raspoređivač GridLayout
GridLayout omogućuje izradu tabele kom ponenata, a kako ih dodajete, one se smeštaju
sleva udesno i odozgo naniže u tabeli. U konstruktoru se zađaje potreban broj redova i
kolona i one se prikazuju u jednakim proporcijam a.

//: gui/GridLayoutl.java
// Prikazuje raspoređivač 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 slučaju postoji 21 polje i samo 20 dugmadi. Poslednje polje je ostalo prazno
zato što GridLayout ne vrši preraspodelu.

Raspoređivač GridBagLayout
GridBagLayout obezbeduje potpunu kontrolu nad odlučivanjem o rasporedu i formati-
ranju oblasti u prozoru kada mu se promeni veličina. Međutim, to je istovremeno i naj-
složeniji raspoređivač koji je veoma teško 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 složen da mislite kako treba koristiti raspoređivač tipa GridBagLayout, trebalo bi
da upotrebite vizuelnu alatku. Ako baš želite da saznate sve detalje o ovome, potražite ih
u nekoj knjizi potpuno posvećenoj grafičkoj biblioteci Svving.
Poglavlje 22: Grafička korisnička okruženja 1059

Umesto raspoređivača GridBagLayout mogli biste da upotrebite TableLayout koji


nije deo biblioteke Swing ali se može preuzeti na adresi http://java.sun.com. Ta kompo-
nenta je izgrađena povrh raspoređivača GridBagLayout i skriva najveći deo njegove
složenosti, pa uveliko pojednostavljuje njegovo korišćenje.

Apsolutno pozicioniranje
Apsolutan položaj grafičkih kom ponenata moguće je zadati i na sledeći način:
1. Uklonite raspoređivač metodom Container.setLayout(null).
2 . U zavisnosti od verzije jezika, za sve komponente pozovite metodu setB ou nd s() ili
resh ap e (), prosleđujući jo j okvirni pravougaonik čije su koordinate izražene pik-
selima. To možete da uradite u konstruktoru ili u metodi p a in t(), u zavisnosti od
toga šta želite da postignete.
Neke alatke za pravljenje grafičkih okruženja često koriste ovakav pristup, ali to obič-
no nije najbolji način za generisanje koda.

Raspoređivač BoxLayout
Pošto korisnici nisu razumevali klasu GridBagLayout i teško su je primenjivali, u biblio-
teku Swing dodata je i klasa BoxLayout koja pruža mnoge prednosti rasporedivača Grid-
BagLayout, a nije toliko složena. Ovakvo raspoređivanje često možete da koristite pri
ručnom razmeštanju elemenata (i ovde, ako program postane previše složen, upotrebite
vizuelnu alatku). Rasporedivač BoxLayout om ogućuje i vertikalno i horizontalno kon-
trolisanje položaja komponenata, a za kontrolisanje prostora izmedu komponenata kori-
ste se ,,podupirači“ i ,,lepak“. Elementarne primere upotrebe rasporedivača BoxLayout
potražite u mrežnim dodacima ove knjige, na adresi www.MindView.net.

Kojije pristup najbolji?


Biblioteka Swing je moćna i može da uradi mnogo toga uz samo nekoliko redova koda. Pri-
meri u ovoj knjizi prilično su jednostavni, a radi učenja vredi ih napisati ručno. Prilično
mnogo se može postići kombinovanjem jednostavnih raspoređivača. Međutim, u određe-
nom trenutku, ručno pisanje koda za grafičko okruženje postaje besmisleno, previše slože-
no i predstavlja traćenje vremena. Projektanti Jave i Swinga orijentisali su jezik i biblioteke
tako da podržavaju alatke za pravljenje grafičkih okruženja i te alatke znatno ubrzavaju
programiranje. Pod uslovom da razumete šta se dešava u raspoređivaču i kako da se izbo-
rite s događajima (što će biti objašnjeno u nastavku), nije naročito važno da zaista znate sve
detalje ručnog raspoređivanja komponenata. Dozvolite odgovarajućoj alatki da to uradi
umesto vas (konačno, Java je i projektovana da bi povećala produktivnost programera).

Model događaja grafičke biblioteke Swing


U modelu dogadaja biblioteke Swing, komponenta može da izazove događaj (engl.fire an
event). Svaka vrsta događaja predstavljena je zasebnom klasom. O događaju se obaveštava
jedan prijemnik (engl. listener) ili više njih a oni potom reaguju na njega. To znači da izvor
1060 Misliti na Javi

događaja i mesto na kom se on obrađuje mogu da se razlikuju. Komponente grafičke bi-


blioteke Swing obično se koriste bez izmene, ali je neophodno pisati kod koji se poziva
kada kom ponente izazovu događaj. To je odličan primer razdvajanja interfejsa i realizacije.
Svaki prijem nik događaja je objekat klase koja realizuje određen tip prijemničkog in-
terfejsa. Zbog toga treba samo da napravite objekat prijemnika i da ga prijavite kom po-
nenti koja pokreće događaj. Prijavljivanje se obavlja metodom addXXXListener( )
komponente čiji se događaj obrađuje, pri čemu ,,X X X “ predstavlja tip događaja koji se
osluškuje. Čitanjem imena metoda tipa „addListener" lako ćete zaključiti koje se vrste do-
gađaja mogu obrađivati. Ako pokušate da primate pogrešne događaje, dobićete grešku
tokom prevođenja. U nastavku poglavlja videćete da zrna Jave takođe koriste metode tipa
,,addListener“ za određivanje događaja koje zrno može da izazove.
Prema tome, sva logika za obradu događaja smešta se u klasu prijemnika. Kada pravite
takvu klasu, jedino ograničenje je da ona mora realizovati odgovarajući interfejs. Prijem -
nička klasa može biti globalna, ali u takvim slučajevima obično su pogodnije unutrašnje
klase. Ne samo da one logički grupišu prijemničke klase unutar korisničkog okruženja ili
klasa poslovne logike kojim a služe, nego i čuvaju referencu na roditeljski objekat, što
predstavlja lep način za prevazilaženje granica oblasti važenja klase i podsistema.
U svim dosadašnjim prim erim a u ovom poglavlju koristio sam model događaja gra-
fičke biblioteke Swing, a sada ćem o detaljnije razmotriti taj model.

Tipovi događaja i prijemnika


Sve Swing komponente sadrže metode tipa ad dX X X Listen er() i rem oveX X X Listen er(),
pa se odgovarajući tipovi prijemnika mogu dođavati u sve komponente i uklanjati iz njih.
Primetićete da oznaka X X X u različitim slučajevima odreduje i argument metode, na pri-
mer: addMojPrijemnik (M ojPrijemnik m). U sledećoj 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() podržavaju pojedine događaje. Uvek treba imati u vidu da se mo-
del događaja može proširivati, pa ćete možda naići na druge tipove prijemnika koji nisu
navedeni u ovoj tabeli.

Događaj, prijemnički interfejs Kom ponenta koja podržava 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: Grafička korisnička okruženja 1061

D ogađaj, prijemnički interfejs Kom ponenta koja podržava taj događaj


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 miša)
MouseListener
ad d M o u seListener()
removeMouseListenerf )
M ouseEvent6 (za pomeranje Klasa Com ponent i klase izvedene iz nje*.
i pritiskanje tastera miša)
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. među 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, uključujuči
TextListener i JTextArea i JTextField
addTextListener( )
removeTextListener( )

Vidi se da svaki tip komponente podržava samo odredene tipove događaja. Ispostavlja
se da je prilično teško tražiti sve događaje koje podržava svaka komponenta. Lakši pristup
je da se prilagodi program PrikaziMetode.java iz poglavlja Podaci o tipu tako da prika-
zuje sve prijem nike đogađaja koje podržava 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 močan zato što automatski prikazuje svemetode

D ogađaj 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 iša uz
držanje pritisnu tog tastera ob jed in jen o je u događaj M ou seE v ent, pa drugo pojavljivanje klase M o-
u seE v en t u tabeli n ije greška.
1062 Misliti na Javi

klase, a da ne morate da prolazite kroz hijerarhiju nasleđivanja i ispitujete osnovne klase


na svakom nivou. Zato ta vredna alatka štedi vreme pri programiranju: pošto su imena
najvećeg broja metoda u Javi opisna i dugačka, tražite metode čija imena sadrže određenu
reč. Kada pronađete metodu koja bi mogla biti ono što tražite, pročitajte njen opis u do-
kum entaciji na Webu.
Međutim, do poglavlja Podaci o tipu nije se pom injala grafička biblioteka Swing, pa je
alatka u tom poglavlju razvijena kao aplikacija koja se izvršava s komandne linije. Evo ko-
risnije verzije za grafičko okruženje koja traži 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: Grafička korisnička okruženja 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));
// Početni 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 sadrži polje ime tipa JTextFielđ; u njega treba da une-
sete ime Swing klase koju tražite. Rezultati se vade pomoću regularnog izraza i prikazuju
u polju tipa JTextArea.
Prim etićete da nema dugmađi niti drugih komponenata kojima bi se naznačilo kako
želite da poćnete pretraživanje. Razlog je to što događaje polja tipa JTextField nadgleda
prijem nik tipa ActionListener. Kad god nešto izmenite i pritisnete taster Enter, spisak se
trenutno ažurira. Ako polje nije prazno, njegov sadržaj se koristi za pokušaj pronalaženja
klase metodom C lass.forN am e(). Ukoliko je ime nepravilno, metoda C lass.forN am e()
neće naći klasu, pa generiše izuzetak koji se hvata, a u objektu klase JTextArea prikazuje
se znakovni niz ,,Ne postoji". Medutim, ako otkucate ispravno ime (važan je i raspored ve-
likih i malih slova), metoda Class.forN am e() nalazi klasu i getM ethods() vraća niz obje-
kata klase Method.
Ovde se koriste dva regularna izraza. Prvi, addListener, traži reč add iza koje slede slova
pa reč Listener i spisak argumenata u zagradi. Ceo regularan izraz je u običnim zagradama
(koje nisu pretvorene u izlazne sekvence), što znači da će biti pristupačan kao ,,grupa“
regularnog izraza kada bude pronaden. Unutar metode Im eL.A ctionPerform ed(), svaki
objekat Method prosleđuje se metodi Uzorak.matcher( ), čime se pravi objekat tipa
Matcher. Kada se za taj objekat tipa Matcher pozove metoda fin d (), ona vraća true ako
pronađe podudaran niz znakova i u tom slučaju pozivom metode g ro u p (l) možete oda-
brati prvu podudarnu grupu unutar zagrada. Taj znakovni niz još uvek sadrži 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
PrikažiMetode.java.
Na kraju konstruktora, u ime se smešta početna vrednost i izvršava se reakcija na do-
gadaj (ActionEvent) koja ispitivanje snabdeva početnim podacima.
1064 Misliti na Javi

Ovaj program je zgodan za ispitivanje mogućnosti neke Swing kom ponente. Kada
znate koje događaje podržava odredena Swing kom ponenta, ne morate da tražite ništa
posebno da bi ona reagovala na taj događaj; samo učinite sledeće:
1. Iz imena klase događaja uklonite reč Event. Dodajte reč Listener onom e što preo-
stane. To je interfejs prijemnika koji morate da realizujete u unutrašnjoj klasi.
2. Realizujete pomenuti interfejs i napišete metode za obradu dogadaja. Na primer,
možda želite da pratite pokrete miša; u tom slučaju treba da napišete 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 prečica, što ćete uskoro
saznati.)
3. Napravite objekat prijemničke 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 prijemničkih interfejsa:

Prijemnički 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: Grafička korisnička okruženja 1065

Ovo nije sveobuhvatan spisak, đelimično i zbog toga što vam model dogadaja dozvo-
ljava da pravite sopstvene tipove dogadaja i prijemnike za njih. Zato ćete često viđati bi-
blioteke u kojim a postoje novi događaji, a znanje koje ste stekli čitajući ovo poglavlje
om ogućiće vam da smislite kako ćete koristiti te događaje.

Korišćenje prijemničkih adaptera radi jednostavnosti


Iz gornje tabele vidi se da neki interfejsi prijem nika sadrže samo jednu metodu. Oni se ve-
oma lako realizuju, pošto se to radi samo kada želite da napišete metodu koju sadrže. Me-
đutim, interfejsi prijemnika ponekad sadrže više metoda koje nije tako prijatno koristiti.
Na primer, kada poželite da uhvatite pritisak na taster miša (koji nije već uhvaćen, recimo,
nekim dugmetom), morate da napišete metodu m ouseC licked(). Ali pošto je MouseLi-
stener interfejs, morate da realizujete i sve ostale metode, čak i ako vam one u tom slučaju
ne koriste. To ume da iznervira.
Da bi se rešio taj problem, neki (ali ne i svi) prijemnički interfejsi koji sadrže više od
jedne metode imaju adaptere , čija imena vidite u prethodnoj tabeli. Svaki adapter obez-
beđuje podrazumevane prazne metode za sve metode interfejsa. U tom slučaju treba
samo da izvedete klasu iz adaptera i da redefinišete samo one metode koje moraju da se
promene. Na primer, tipičan interfejs MouseListener koji ćete koristiti izgleda ovako:

class MojPrijemnikMisa extends Mo useAdapter {


public void mouseClicked (MouseEvent e) {
// Odgovor na pritisak tastera miša...
}
}

Smisao adaptera i jeste da olakša pravljenje prijemničkih klasa.


Ipak, adapteri imaju i jedan nedostatak. Pretpostavimo da ste nasledili klasu Mouse-
Adapter na ovaj način:

class MojPrijemnikMisa extends Mo useAdapter {


public void MouseClicked (MouseEvent e) {
// Odgovor na pritiskanje tastera miša...
}
}

Ovo ne radi, a vi ćete izludeti pokušavajući da otkrijete razlog. Sve se lepo prevodi i iz-
vršava, osim što nakon pritiska na taster miša vaša metoda neće biti pozivana. Vidite li šta
je problem? Ime metode: navedeno je M ouseClicked() umesto m ouseC licked(). Malo
zanemarivanje veličine slova prouzrokuje dodavanje potpuno nove metode. Međutim, to
nije metoda koja se poziva kada se prozor zatvara, pa nećete 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 ređefinisati odredenu metodu ukoliko u gornjem kodu upotrebite ugra-
đenu anotaciju @Override.
Vežba 9: (5) Na osnovu programa PrikaziMetodeAddListener.java napišite program sa
svim funkcijama programa podaciotipu.PrikaziM etode.java.
1066 Misliti na Javi

Praćenje više događaja


Da bismo dokazali da se ovi dogadaji zaista dešavaju, napisaćemo program koji prati do-
datno ponašanje 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 unutrašnia klasa u klasi PratiDogadjaj, pa MojeDugme ima pri-
stup roditeljskom prozoru i može da radi s njegovim poljim a za tekst, što je neophodno da
bi statusne informacije mogle da se upisuju u polja roditelja. Naravno, ovo rešenje ima
ograničenja, pošto objekat te klase može 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 događaje koji se dešavaju.
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 oguće izvesti korisne klase iz dugm eta. To je bio sam o jedan od m nogih ve-
likih propusta u projektovanju.
Poglavlje 22: Grafička korisnička okruženja 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 sadrži mapu tipa HashMap. Ona povezuje znakovne nizove koji
predstavljaju tip događaja i polja tipa JTextField u kojima se nalaze informacije o doga-
daju. Naravno, ova polja su mogla da budu i statička, ali složičete se da se ovako mnogo
lakše koriste i m enjaju. Poseban je slučaj ako dodajete ili uklanjate nov tip đogađaja 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 događaja i parametarski znakovni niz iz događaja.
Ova metoda koristi HashMap mapu h iz spoljašnje klase da bi pronašla tekstualno polje
povezano s im enom događaja, a zatim smešta parametarski znakovni niz u to polje.
Ovaj prim er je koristan pošto možete da vidite šta se u programu stvarno dešava sa do-
gadajima.
Vežba 10: (6) Napravite aplikaciju uz pomoč klase SwingKonzola s dugmetom tipa
JButton i poljem tipa JTextField. Napišite i povežite odgovarajući prijem nik koji u polju
JTextField prikazuje sadržaj dugmeta kada je ono u fokusu.
Vežba 11: (4) Izvedite nov tip dugmeta iz klase JButton. Kad god se to dugme pritisne,
trebalo bi da promeni boju u neku nasumično izabranu. Primer kako da generišete
nasum ičnu boju potražite u programu ObojeneKutije.java u nastavku poglavlja.
Vežba 12: (4) Obradite nov tip događaja u programu PratiD ogadjaj.java dodavanjem
novog koda za obradu događaja. Moraćete sami da smislite tip događaja koji želite da
obrađujete.

Primeri Svving komponenata


Sad razumete raspoređivače i model događaja i spremni ste da vidite kako se mogu kori-
stiti kom ponente grafičke biblioteke Swing. Ovaj odeljak ukratko prikazuje Swing kom-
ponente i njihove mogućnosti koje ćete verovatno najčešće koristiti. Primeri nisu previše
obim ni, pa kod možete da iskoristite u sopstvenim programima.
Im ajte u vidu sledeće:
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 (možete 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: Grafička korisnička okruženja 1069

3 . Zbog pravila za imenovanje koja se prim enjuju na Swing događaje, prilično se lako
pogađa kako se piše i registruje prijem nik za odredeni tip dogadaja. Upotrebite
program PrikaziMetodeAddListener.java iz ovog poglavlja da biste sebi olakšali
traženje određene komponente.
4 . Kada stvari postanu komplikovanije - vreme je da koristite vizuelne alatke.

Dugmad
Biblioteka Swing sadrži veliki broj dugmadi različitih 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 nešto opštije).
Uskoro ćete saznati kako se koriste stavke menija, a sledeći primer prikazuje različite 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 izvršite program, videćete da dugme za uključi-
vanje i isključivanje zadržava svoj poslednji položaj (uključeno ili isključeno). Ali polja za
potvrdu i radio-dugmad ponašaju se jednako - mogu da budu potvrđeni ili nepotvrđeni
(izvedeni su iz klase JToggleButton).

Grupe dugmadi
Ako želite da radio-dugmad isključuju jedno drugo, morate ih smestiti u „grupu dugma-
di“. Međutim, kao što prikazuje sledeći primer, svako dugme tipa AbstractButton može
se dodati grupi tipa ButtonGroup.
Da bi se izbeglo često ponavljanje koda, u ovom primeru koristi se refleksija za pra
vljenje grupa dugmadi različitih 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
određuje prvi argument metode:

//: giri/GrupeDugmadi.java
// Primenjuje refleksiju za pravljenje grupa
// različitih 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 dinamičkog 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: Grafička korisnička okruženja 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


početku je dugme ab tipa AbstractButton u stvari JButton sa oznakom ,,neuspešno“, pa
čete lako uočiti eventualne probleme, čak i ako zanemarite poruke o izuzecima. Metoda
getC onstructor( ) vraća objekat tipa C onstructor koji opisuje konstruktor čija lista ar-
gumenata odgovara nizu tipova u spislcu ldasa prosleđenom toj metodi. Nakon toga treba
samo da pozovete metodu newlnstance( ) kojoj ćete proslediti niz objekata koji sadrži
stvarne argumente; u ovom slučaju, samo objekte tipa String iz niza ids.
Da biste postigli „isključivo" ponašanje dugmadi, napravite grupu dugmadi i dodajte
jo j svu dugmad koja tako treba da se ponašaju. Kada izvršite program, videćete da svi
tipovi dugmadi osim JButton realizuju „isključivo" ponašanje.

Ikonice
Klasu Icon možete da koristite unutar natpisa tipa JLabel ili bilo koje klase koja se izvođi
iz klase AbstractButton (uključujući JButton, JCheckBox, JRadioButton i razne vrste
JM enuItem ). Korišćenje ikonica s klasom JLabel sasvim je jednostavno (kasnije ćete vi-
deti prim er). U sledećem primeru istražuju se drugi načini korišćenja ikonica s dugma-
dima i objektim a koji su iz njih izveđeni.
Možete da koristite proizvoljne datoteke u formatu GIF, a one koje se upotrebljavaju u
ovom primeru naći ć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, moći ćete u programu da koristite dobijeni
objekat tipa Icon.

//: g u i / L i k o v i .java
j j Ponašanje 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: Grafička korisnička okruženja 1073

O bjekat klase Icon može se koristiti u m nogim konstruktorim a grafičkih elemenata,


ali za dodavanje ili uklanjanje ikonice možete da koristite i metodu se tIco n (). Ovaj pri-
mer pokazuje i kako dugme tipa JButton (ili bilo koji objekat klase AbstractButton)
može da prikazuje različite vrste ikonica u zavisnosti od događaja: kada se pritisne, isklju-
či ili kada se pokazivač miša zadrži iznad njega bez pritiskanja. Videćete da se tako dobija
zanimljivo, animirano dugme.

Priručni saveti
U prethodnom primeru dugmetu je dodat i „priručni savet“ (engl. tool tip). Gotovo sve
klase grafičkih elemenata izvedene su iz klase JCom ponent koja sadrži metodu setTool-
TipText(String). Zbog toga za doslovno sve što stavite na obrazac, treba samo da napišete
(za objekat jc bilo koje klase izvedene iz klase JC om ponent):

jc.setToolTipText("Moj sa v e t " ) ;

i kada se pokazivač miša zadrži iznad objekta klase JCom ponent zadato vreme, pored po-
kazivača će se pojaviti malo polje s vašim tekstom.

Jednoredna polja za tekst


Ovaj primer prikazuje dodatno ponašanje polja za tekst tipa JTextField:

//: g u i/ Po lj aZ aT ek st.java
// Polja za tekst i Javini događaji.
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: Grafička korisnička okruženja 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 služi za izveštavanje o događajima polja t l tipa JTextFieId. Vi-
dećete da se prijemnik događaja za polje JTextField pokreće 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 slučaju, sadržaja 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, briše sadržaj i pomera pokazivač, pa sve radi kao što se i
očekuje.
Vežba 13: (3) Promenite program PoljaZaTekst.java tako da znakovi u t2 zadržavaju pr-
vobitni raspored malih i velikih slova kako su bila unesena, umesto da se automatski pre-
tvaraju u velika slova.

Ivice
Klasa JComponent sadrži metodu se tB o rd e r() koja om ogućuje postavljanje zanimljivih
ivica oko svih vidljivih komponenata. Sledeći 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 izvršavanja 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);
}
} ///:-

Možete da napravite i sopstvene ivice koje ćete staviti oko dugmadi, natpisa ili bilo
čega što je izvedeno iz klase JCom ponent.

Mali program za uređivanje teksta


Klasa JTextPane obezbeduje najveći deo podrške za obradu teksta bez mnogo truda. Sle-
deći primer ilustruje veoma jednostavnu upotrebu, bez mnogih funkcija ove klase:

//: gui/TekstualniPano.java
// Klasa JTextPane kao mali program za uređivanje 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: Grafička korisnička okruženja 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 pažnju na ugrađene mogućnosti klase JTextPane kao što je automatsko pre-
lamanje redova. Postoje i brojne druge funkcije o kojima možete mnogo saznati iz doku-
m entacije razvojnog programskog paketa JDK.
Vežba 14: (2) Promenite program TekstualniPano.java tako da koristi polje tipa JText-
Area umesto JTextPane.

Polja za potvrdu
Polje za potvrdu om ogućuje pravljenje opcije koja može da bude potvrdena ili nepotvr-
đena; sastoji se od malog polja i oznake. Polje obično sadrži malo x (ili nešto drugo što po-
kazuje da je opcija potvrđena) ili je prazno, u zavisnosti od toga da li je stavka koju
označava potvrđena ili nije.
O bjekat klase JCheckBox obično se pravi pom oću konstruktora čiji je argument nat-
pis. Stanje polja možete da čitate i zadajete, ali možete i da čitate i menjate njegov natpis
nakon pravljenja.
Kad god se polje tipa JCheckBox potvrdi ili se potvrda ukloni, nastaje događaj koji
možete da uhvatite na isti način kao događaj dugmeta, pomoću objekta klase ActionLi-
stener. U sledećem primeru koristi se tekstualno polje za brojanje potvrđenih polja:

//: gui/PoljaZaPotvrdu.java
// Korišćenje 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 tekuće stanje u sadržaj višerednog polja
za tekst metodom ap p en d (), pa će se prikazati zbirni spisak odabranih polja i njihovih
stanja.
Vežba 15: (5) Dodajte polje za potvrdu u aplikađju napravljenu u vežbi 5, uhvatite doga-
đaj i umetnite drugačiji tekst u polje.

Radio-dugmad
Ime radio-đugme u programiranju grahčkih korisnićkih okruženja izvedeno je aso-
cijacijom na radija iz starinskih autom obila koji su imali mehaničku dugmad. Kada bi se
jedno dugme pritisnulo, iskakalo bi dugme koje je ranije bilo pritisnuto. Na taj naćin mo-
gla je da se odabere samo jedna mogućnost.
Poglav[je 22: Grafička korisnička okruženja 1079

Da biste napravili povezanu grupu dugmadi tipa JRadioButton, treba samo da ih do-
date u grupu tipa ButtonGroup. (U obrascu može da postoji proizvoljan broj grupa dug-
madi.) Jednom dugmetu može da se (drugim argumentom konstruktora) zada početno
stanje tako da bude potvrđeno. Ako više radio-dugmadi u grupi uključite konstruktorom,
poslednje dugme će iskJjučiti sva prethodna.
Evo jednostavnog prim era korišćenja radio-dugmadi. O bratite pažnju na to da se do-
gađaji radio-dugmadi obrađuju isto kao i svi drugi događaji:

//: gui/RadioĐugmad.java
// Korišćenje 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 (padajuće liste)


Poput grupe radio-dugmadi, padajuća lista omogućava da se korisnik navede da izabere
samo jedan element iz grupe mogućnosti. Međutim, lista je kom paktnije rešenje da se to
postigne, a elemente liste je lakše m enjati tako da se korisnik ne iznenadi. (Radio-dugmad
možete menjati dinamički, ali to ume i da smeta.)
Klasa JCom boBox podrazumevano se ne ponaša kao kombinovana lista u Windowsu
koja omogućuje da korisnik odabere stavku iz liste ili da u nju sam unese neku vrednost,
ako nije zadovoljan ponuđenim opcijama. Da biste omogućili korisnicim a da unose vred-
nosti u objekat tipa JCom boBox, potrebno je da pozovete metodu setEditable. Iz liste
tipa JCom boBox korisnik može odabrati jedan i samo jedan element. U sledećem prime-
ru, polje tipa JCom boBox na početku sadrži određen broj stavki, a zatim se nakon priti-
skanja dugmeta u polje dodaju novi elementi.

//: gui/PadajuceListe.java
// Korišćenje padajućih 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",
"Zastrašujući" , "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: Grafička korisnička okruženja 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.

Grafičke liste
Polja tipa JList prilično 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 tačno
određen broj redova na ekranu i ne m enja se. Ako želite da vidite elemente liste, samo po-
zovite metodu getSelectedValues() koja vraća niz izabranih stavki.
Lista JList omogućuje višestruki izbor: ako uz pritisnut taster Ctrl mišem pritisnete
više od jedne stavke, stavka koj u ste prvu izabrali ostaće istaknuta, a nakon toga možete da
izaberete još proizvoljan broj elemenata. Ukoliko izaberete neki element, a zatim uz pri-
tisnut taster Shift mišem pritisnete još jedan element, biće izabrani svi elementi između
njih. Da biste uklonili stavku iz izabrane grupe, pritisnite je mišem, istovremeno držeći
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 grozđice", "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 {
// Isključi, pošto nema više 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 sadržaja radi prikaza
add(t);
ad d ( l s t ) ;
add(b);
// Registrovanje slušalaca događaja
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);
}
} ///:-

Primećujete i da su listama dodate ivice.


Ako želite samo da smestite niz objekata tipa String u listu tipa JList, postoji mnogo
jednostavnije rešenje: 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 izvršavanja programa.
Liste tipa JList ne obezbeđuju automatski direktnu podršku za pomeranie sadržaja po
ekranu. Naravno, treba samo da stavite listu u okno tipa JScroiiP ane i njemu prepustite
da se brine o detaljima.
Vežba 16: (5) Pojednostavite program Lista.java prosledivanjem niza konstruktoru
i uklanjanjem dinamičkog dodavanja elemenata u listu.
Poglavjje 22: Grafička korisnička okruženja 1083

Okno s jezičcima
Klasa JTabbedPane om ogućuje pravljenje okna u kom e se duž gornje ivice prikazuju je-
zičci. Pritiskom na odredeni jezičak prikazaćete njegov poddijalog.

//: gui/ OknoSaJeziccima l.java


// Prikazuje okno s jezičcima.
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 grozđice", "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 jezičcima" + i++));
jezicci.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent e) {
txt.setText("Izabrani jezičak kartice: " +
jezicci,getSelectedIndex());
}
});
add(BorderLayout.SOUTH, txt);
add(jezicci);
}
public static void main(String[] args) {
run(new OknoSaJeziccimal(), 400, 250);
}
} ///: -

Kada izvršite program, videćete da JTabbedPane autom atski prenosi jezičke u sledeći
red ako ih ima previše da bi stali u isti red. To ćete vidieti kada u toku rada prom enite ve-
ličinu 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 adrži 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 grafičkoj biblio-
teci Svving, polja za poruke realizuje klasa JOptionPane. Postoje brojne m ogućnosti (od
kojih su neke vrlo napredne), ali najčešće ćete koristiti dijalog poruke i dijalog za potvrdu
koji se dobijaju pozivom statičkih m etoda JO ptionPane.showM essageDialog( ) i JOpti-
onPane.show C onfirm D ialog( ). Sledeći 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 grešku!", "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: Grafička korisnička okruženja 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 đogađaja, koristio sam donekle rizičnu proveru
natpisa na dugm adim a. Pri takvoj proveri često se pogreši u pisanju, obično u rasporedu
velikih i m alih slova, a takva greška se teško uočava.
Obratite pažnju na to da su rezultati m etoda sh ow O ption D ialog( ) i showInputDia-
lo g ( ) objekti s vrednošću koju je uneo korisnik.
Vežba 17: (5) Napravite aplikaciju p o m oću klase SwingKonzola. U HTML dokum enta-
ciji na lokaciji java.sun .com pronađite klasu JPasswordField i dodajte je u program. Ako
korisnik otkuca tačnu lozinku, prikažite poruku o uspehu p om oću objekta klase JOpti-
onPane.
Vežba 18: (4) Prom enite program PoljaZaPotvrdu.java tako da ima zaseban prijemnik
tipa ActionListener za svako dugm e (um esto da traži tekst na dugm etu).

Meniji
Svaka kom ponenta koja m ože da sadrži m eni, uključujući JApplet, JFrame, JDialog i kla-
se koje su iz njih izvedene, im a m etodu setJMenuBar( ). Toj m etodi se prosleđuje linija
menija - objekat klase JMenuBar (m ože da postoji sam o jedan objekat klase JMenuBar
za određenu 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 ože da bude povezan poseban prijem nik događaja, i on se aktivira kada se izabere
ta stavka menija.
Kada koristite Javu i biblioteku Swing, m orate ručno da definišete 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 korišćenja operatora m ođulo u izrazu i%3 ravnom erno se raspoređuju 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 ičn o potreban poseban prijemnik tipa za sva-
ku stavku.
Klasa JMenuItem je izvedena iz klase AbstractButton, pa se ponaša donekle slično
dugm etu. Ona obezbeđuje stavku koja se m ože staviti u m eni. Postoje i tri tipa izvedena
iz klase JMenuItem: JMenu za čuvanje drugih stavki tipa JMenuItem (m oguće je napra-
viti kaskadne m enije), JCheckBoxMenuItem koja sadrži polje za potvrdu što pokazuje da
li je stavka m enija izabrana i JRadioButtonMenuItem koja sadrži radio-dugm e.
Kao napredniji prim er za pravljenje menija p o n o v o ćem o razmotriti različite ukuse
sladoleda. Ovaj prim er prikazuje i kaskadne m enije, prečice s tastature, stavke menija
s poljim a za potvrdu i način za dinam ičko m enjanje menija:

//: gui/Meniji.java
// Podmeniji, polja za potvrdu u meniju, kaskadni
// meniji, prečice i komande za pokretanje.
import javax.swing.*;
Pog!av[je 22: Grafička korisnička okruženja 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 grozđice", "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 prečice 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 prečice:
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(); // Osvežavanje 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()) ;
}
} ^
// Možete i da napravite zasebnu klasu
// za svaku stavku tipa Menultem.
// Tada nećete 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: Grafička korisnička okruženja 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 određenim 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);
// Podešavanje 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 nešto lakše.
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 izvršava. 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 ože se prikazati u tekućem prozoru m etodom setJMe-
nuBar( ). Obratite pažnju 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 važni prilikom ispitivanja natpisa
O tvori, ali Java ne ukazuje na grešku ako ne nađe odgovarajuću stavku. Ovakva vrsta po-
ređenja znakovnih nizova čest je izvor grešaka u programiranju.
Potvrđivanje i odjavljivanje stavki m enija obavlja se autom atski. Kod kojim se obra-
đuju stavke tipa JCheckBoxMenuItem prikazuje dva načina za određivanje onoga što je
potvrđeno: pronalaženje istih znakovnih nizova (kao što je rečeno, to nije naročito bez-
bedan pristup iako se s vrem ena na vrem e koristi) i pronalaženje istih ciljnih objekata do-
gađaja. Kao što je pokazano, m etoda getState( ) m ože se koristiti za ustanovljavanje
stanja. Stanje stavke JCheckBoxMenuItem m ožete da prom enite i m etodom setS tate().
Događaji 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 podrže i prijemnik
tipa ActionListener, ali to ob ičn 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 podržava prečice s tastature, što znači da m ožete aktivirati bilo koji
objekat klase izvedene iz klase AbstractButton (dugm e, stavku menija itd.) korišćenjem
tastature um esto miša. To je prilično jednostavno: za stavku JMenuItem m ožete da upo-
trebite preklopljeni konstruktor čiji je drugi argum ent identifikator tastera. M eđutim , ve-
ćina dugm adi tipa AbstractButton nem a takve konstruktore, pa je opštiji način za
rešavanje problem a da se koristi m etoda setM nem on ic( ). U prethodnom primeru doda-
te su prečice u neke stavke menija; indikatori prečica autom atski se pojavljuju na kom po-
nentam a.
Vidi se i korišćenje m etode setA ctionC om m and( ) kojom se zadaje kom anda akcije. To
je m alo neobično zato što je u svim slučajevim a „komanda akcije" ista kao natpis na kom -
p onenti m enija. Zašto se ne upotrebi postojeći natpis um esto novog znakovnog niza?
Zbog internacionalizacije. Ako ovaj program prilagodite drugom jeziku, menjaćete sam o
natpise u m eniju, ali ne i kod (to bi bez sum nje prouzrokovalo nove greške). Da bi se kod
koji traži znakovne nizove povezane s kom ponentam a menija lakše izvršavao, „komanda
akcije“ je neprom enljiva, dok se natpis menija m ože menjati. Ceo kod radi s„kom andom
akcije", pa na njega ne utiću prom ene natpisa menija. Obratite pažnju 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: Grafička korisnička okruženja 1091

Najveći 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 događaja (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 obrađuje sve različite 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 tačno ko je
pozvao prijem nik. Čak i ako se broj klasa koje su napravljene na ovaj način poveća, kod
unutar njih nije dugačak pa je ovakav postupak bezbedniji.
V idite da kod menija brzo narasta i postaje neuredan. I u ovom slučaju treba koristiti
vizuelnu razvojnu alatku. Dobra alatka te vrste istovrem eno će i održavati menije.
Vežba 19: (3) Prom enite program Meniji.java tako da se u m eniju koriste radio-dugm ad
um esto polja za potvrdu.
Vežba 20: (6) N apišite program koji tekstualnu datoteku deli na reči. Stavite te reči u m e-
nije i podm enije kao stavke.

Iskačući meniji
Najjednostavniji način za prim enu iskačućeg menija tipa JPopupMenu jeste da se napra-
vi unutrašnja klasa koja proširuje klasu MouseAdapter, i da se zatim po jedan objekat te
unutrašnje klase pridruži svakoj kom ponenti koja treba da izaziva iskakanje menija:

//: gui/Iskakanje.java
// Pravljenje iskačućih menija pomoću 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 eće ga u tekstualno polje.

Crtanje
U dobroj biblioteci za projektovanje grafičkih okruženja 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 izračunavanja kojima se zadaje gde če se šta crtati ob ično m n ogo složenija od poziva
procedura za crtanje. Izračunavanja su često pom ešana s p ozivim a procedura, pa pristup
izgleda složeniji nego što stvarno jeste.
Radi jednostavnosti, razm otrim o problem s predstavljanjem podataka na ekranu;
ovde će podatke obezbeđivati ugrađena m etoda M a th .s in f), ti. matematička 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 biće postavljen klizač za dinam ičko
kontrolisanje broja prikazanih perioda sinusoide. Pored toga, ako prom enite veličinu
prozora, videćete da se sinusoida prilagođava toj novoj veličini.
Poglav[je 22: Grafička korisnička okruženja 1093

Iako se bilo koja kom ponenta tipa JComponent m ože koristiti kao platno za crtanje,
ako želite jednostavnu površinu (pano) za crtanje, obično 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 bično ne m orate da brinete o tom e kada će ova m etoda
biti pozvana, pošto o tom e odlučuje biblioteka Swing.) Swing prosleđuje ovoj m etodi obje-
kat tipa Graphics, a taj objekat zatim m ožete da koristite za crtanje ili slikanje p o površini.
U sledećem prim eru se sva logika povezana s crtanjem nalazi u klasi CrtajSinusoidu;
klasa Sinusoida sam o podešava program i klizač. M etoda zadajCikluse( ) om ogućuje
drugom objektu, u ovom slučaju klizaču, da kontroliše broj ciklusa.

//: gui/Sinusoida.java
// Crtanje u biblioteci Swing, korišćenjem klizača 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 izračunavanjima tačaka sinusoide: promenljiva
ciklusi označava broj potpunih ciklusa sinusoida koji treba da budu prikazani, tacke sa-
drži ukupan broj tačaka, sinusi sadrži vrednosti sinusne funkcije, a tck sadrži Y koordi-
nate tačaka koje se crtaju na panou. M etoda zađajCikIuse( ) pravi nizove na osnovu broja
potrebnih tačaka 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 izračunavanje i pon ovn o iscrtavanje.
Kada redefinišete m etodu paintCom ponent( ), prvo m orate da pozovete verziju te
m etode iz natklase. Nakon toga m ožete da radite šta god želite; ob ičn o se za crtanje i bo-
jenje piksela na panou JPanel koriste m etode klase Graphics koje m ožete da pronađete u
dokum entaciji za biblioteku java.awt.Graphics (na adresi h ttp ://ja va.sun .com ). Primeti-
ćete da je ovde najveći deo koda posvećen izračunavanjima; jedine dve m etode koje zaista
crtaju su setC olor( ) i drawLine( ). V erovatno će tako biti i u vašim program im a koji pri-
kazuju grafičke podatke: najveći deo vrem ena provodićete smišljajući š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 utrošio na to da postignem da se si-
nusoida prikaže. Čim sam uspeo, palo m i je na pam et da bi bilo lepo kada bi broj perioda
m ogao đinam ički d.i se meni.i. Zbog iskustava iz drugih p n lgramskih jezika oklevao sam
da to uradim, ali se ispostavilo da je to najlakši deo posla. Napravio sam klizač - objekat
klase JSlider (argum enti su krajnja leva vrednost, krajnja desna vrednost i početna vred-
nost klizača, ali ima i drugačijih konstruktora) i ubacio sam ga u klasu JFraine. Zatim sam
pregledao HTML dokum entaciju i prim etio da je jedini prijem nik klizača addChangeLi-
stener. On dobija informaciju kad god se položaj klizača prom eni. Jedina m etoda tog
Poglav[je 22: Grafićka korisnička okruženja 1095

interfejsa je stateC hanged( ), što se m oglo zaključiti po im enu prijemnika. Njen argu-
m ent je objekat tipa ChangeEvent koji om ogućuje da se pregleda izvor prom ene i pročita
nova vrednost. Pozivanjem m etode zadajCikluse( ) objekta sinusi p on ovo se iscrtava
pano s novom sinusoidom .
V idećete da se većina problem a u biblioteci Swing m ože rešiti sličnim postupkom , što
je ob ičn o dosta lako, čak i ako neku k om p on en tu nikada ranije niste koristili.
Ako su vaši problem i složeniji, postoje i naprednije m etod e za crtanje, uključujući i
zrna Jave nezavisnih proizvođača i interfejs za programiranje Java 2D. Ta rešenja prevazi-
laze okvire ove knjige, ali bi trebalo da ih potražite ako vaš kod za crtanje postane previše
problem atičan.
Vežba 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.
Vežba 22: (7) Napravite aplikaciju p o m o ću klase SwingKonzoIa. Trebalo bi da im a tri
klizača, 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 klizača. Postavite i polja
za tekst čiji se sadržaj ne m ože menjati i koja prikazuju tekuće RGB vrednosti.
Vežba 23: (8) Na osnovu aplikacije Sinusoida. java napravite program koji na ekranu
prikazuje rotirajući kvadrat. Neka jedan klizač upravlja brzinom rotacije, a drugi veliči-
n om prozora.
Vežba 24: (7) Sećate li se igračke s dva dugm eta - jednim koje kontroliše vertikalno kre-
tanje nacrtane tačke i drugim koje kontroliše horizontalno kretanje? Napravite takvu
igračku koristeći program Sinusoida. java. U m esto dugm adi upotrebite klizače. Dodajte
dugm e kojim se briše cela skica.
Vežba 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 pokreće
klasa java.util.Timer, a njenu brzinu neka određuje kontrola javax.swing.JSlider.
Vežba 26: (3) Izm enite prethodnu vežbu tako da aplikacija im a više panela sa sinusoida-
ma. Broj panela neka se zadaje parametrom na kom andnoj liniji.
Vežba 27: (5) Izm enite vežbu 25 tako da anim aciju pokreće klasa javax.swing.Timer.
Obratite pažnju na razliku izm eđu ove anim acije i o n e u kojoj se koristi klasa ja-
va.util.Timer.
Vežba 28: (7) Napravite klasu za kockanje (bez grafičkog korisničkog okruženja). Neka se
baca pet kocaka i to više puta. Nacrtajte krivu koja prikazuje zbir palih brojeva i dinam ič-
ki se ažurira nakon svakog bacanja.

Okviri za dijalog
Okvir za dijalog je prozor koji iskače iz drugog prozora. N jegova svrha je da se izbori sa
određenom tem om a da ne zatrpa detaljima početni prozor. Dijalozi se često koriste u
grafičkim program skim okruženjima.
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 raspoređivač (podrazum evani
je BorderLayout), a za obradu događaja treba da dodate prijemnike. Evo vrlo jednostav-
n o g primera:

//: gui/Dijalozi.java
// Pravljenje i korišćenje 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: Grafička korisnička okruženja 1097

Sledeći prim er je složeniji. Dijalog sadrži tabelu dugm adi tipa Ik sO k sD u gm e (napra-
vljena je p o m oću klase GridLayout). Ovo dugm e oko sebe iscrtava okvir i, u zavisnosti od
svog stanja, u sredini ne prikazuje ništa, prikazuje x ili o. D u gm e je u početku prazno, a
zatim se, u zavisnosti od toga ko je na redu da igra, pretvara u x ili o. M eđutim , on o na-
izm en ično menja stanja izm eđu x i o i kada ga pritiskate. (N a taj način igra iks-oks se
m alo kom plikuje.) Pored toga, okvir za dijalog m ože da sadrži 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; // Počinje 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);
}
} ///:-

Pošto sam o spoljašnji nivo klase m ože imati statičke ćlanove, unutrašnje klase ne
m ogu da sadrže statičke podatke ili ugnežđene 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 izračunavanja, ali je jednostavan.
Poglavlje 22: Grafička korisnička okruženja 1099

Operaciju pritiskanja miša hvata objekat klase MouseListener koji prvo proverava da
li je na p an ou nešto 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 oću m ehani -
zma unutrašnje klase, IksOksDugm e zatim pristupa svom roditelju i menja igrača koji je
na potezu. Ako dugm e već prikazuje x ili o, stanje m u se menja. U izračunavanjima se vidi
zgodno korišćenje 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 prilično jednostavan: u tabelu tipa GridLayout
dodaje dugm ad po potrebi, a zatim povećava stranu svakog dugm eta za 50 piksela.
Objekat klase IksOks pokreće celu aplikaciju tako što pravi tekstualna polja za unošenje
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 pošto su to znakovni nizovi, m o-
raju se konvertovati u tip int p om oću Integer konstruktora koji prima String argument.

Dijalozi za rad s datotekama


Neki operativni sistem i im aju veliki broj posebnih ugrađenih dijaloga koji služe za biranje
fontova, boje, štampača i sl. G otovo svi grafički operativni sistem i imaju i dijaloge za
otvaranje i snim anje datoteka, pa i Javina klasa JFileChooser kapsulira takve dijaloge radi
lakšeg korišćenja.
U sledećoj aplikaciji prikazuju se dva oblika dijaloga tipa JFUeChooser, jedan za otva-
ranje i drugi za snim anje. Najveći deo koda do sada bi trebalo da vam bude poznat, a sve
zanim ljive aktivnosti dešavaju se u prijem nicim a događaja za dva različita 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, uključujući i one koje kori-
ste filtre za sužavanje 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 ožete da čitate podatke
iz njega. M etode getSelectedF ile( ) i getC urrentD irectory( ) dva su načina za ispitivanje
rezultata operacije. Ako te m etode vrate null, korisnik je u dijalogu pritisnuo dugm e za
odustajanje (C ancel).
Poglavlje 22: Grafička korisnička okruženja 1101

Vežba 29: (3) U HTML dokum entaciji za biblioteku java.sw in g potražite klasu JCoIor-
C hooser. N apišite 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 ože da prikaže i HTM L tekst koji će se form a-
tirati u skladu s HTML pravilim a. To znači da Swing kom ponenti m ožete 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 započne oznakom < h tm l> , a potom m ožete da upotrebljavate uobiča-
jene HTML oznake. Obratite pažnju na to da nije obavezno korišćenje uobičajenih 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 eđutim , taj nalpis se ne đođaje 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 ožete da koristite i u objektim a klasa JTabbedPane, JM enuItem , JTooI-
Tip, JR adioB utton i JCheckBox.
Vežba 30: (3) Napišite program u kojem se HTML tekst ispisuje na svim objektima
nabrojanim u prethodnom pasusu.
1102 Misliti na Javi

Klizači i linije napredovanja


Klizač (engl. slider) koji je već upotrebljen u prim eru sinusoide, om ogućuje korisniku da
unosi podatke tako što ga pom era napred-nazad, što je u nekim situacijama intuitivno
(na primer, za kontrolu jačine 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č zakači za traku napredovanja tako da se saglasno pom eranju
klizača pom era i traka napredovanja. U narednom prim eru videćete i P rogressM onitor,
što je bogatiji iskačući dijalog :

//: gui/Napredak.java
// Korišćenje traka napredovanja, klizača 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()); // Zajednički 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. Grafička korlsnička okruženja 1103

Ključ za povezivanje klizača i trake napredovanja leži u zajedničkom korišćenju m o-


dela, u redu:

pb.setModel(sb.getModel());

Ove dve kom p onente m ogli biste da kontrolišete i p om oću prijemnika, ali je ovo reše-
nje prostije. ProgressM onitor nem a m odel, pa sam m orao da upotrebim prijemnik. Ve-
rovatno ste prim etili da se ovaj ProgressMonitor kreće sam o unapred i da se zatvara kada
d ođe do kraja.
Klasa JProgressBar je prilično očigledna, ali klasa JSlider im a m noštvo opcija, npr.
orijentisanje, veće i manje zareze na klizaču itd. Obratite pažnju na to kako se lako dodaje
ivica sa natpisom .
V ežba31: (8) Napravite „indikator asim ptotskog napredovanja“ koji se usporava s pribli-
žavanjem završnoj tački. Dodajte mu nasum ično pogrešno ponašanje da bi s vremena na
vrem e izgledalo kao da počinje da se ubrzava.
Vežba 32: (6) Izm enite program Napredak.java tako da za povezivanje klizača i trake na-
predovanja ne koristi zajedničke m odele, već prijemnik.

Biranje izgleda i ponašanja


Jedna od zanim ljivih m ogućnosti grafičke biblioteke Swing jeste postizanje promenijivog
izgleda i ponašanja (engl. Plnggable L ookik Feel). To om ogućuje programu da simulira iz-
gled i ponašanje različitih radnih okruženja. M ožete da uradite zanimljive stvari, npr. da
dinam ički menjate izgled i ponašanje programa. M edutim , obično ćete koristiti sam o
jednu ili dve m ogućnosti: biraćete 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 ponašanje pri-
lično je jeđnostavan, ali m orate da ga izvršite prc nego što počnete da pravite vizuelne
kom ponente. K om ponente se prave na osnovu tekućeg izgleda i ponašanja, tj. postojeće
k om p onen te neće biti prom enjene sam o zato što ste vi usred programa odlučili da im
prom enite izgleđ (takav postupak je složeniji i neuobičajen, a obrađen je u knjigama po-
svećenim grafičkoj biblioteci Swing).
Zapravo, ako želite da program izgleđa isto na svim platform am a, ne m orate ništa da
radite, jer se „metalni" izgled podrazum eva. Ali ako želite da koristite izgled i ponašanje
tekućeg radnog okruženja,Rtreba sam o da um etnete sledeći kod, obično na početak me-
tode m a in ( ); obavite to pre pravljenja bilo kakvih grafičkih 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 ože da bude prazan zato što će U IM anager učitati podrazum evan izgled
i ponašanje ako pokušaj da zadate drugačiji izgled ne uspe. M eđutim , inform acija o izu-
zetku biva veom a korisna pri traženju grešaka, pa u bloku catch m ožete staviti barem na-
redbu za ispisivanje.
Evo programa koji na osnovu argum enta s kom andne linije zadaje izgled i ponašanje,
i pokazuje kako neke kom ponente u tom slučaju izgledaju:

//: gui/IzgledlPonasanje.java
// Biranje različitih izgleda i ponašanja.
// {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(
"Korišćenje: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: Grafička korisnička okruženja 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 pažnju na to da se izgled i ponašanje moraju
// zadati pre pravljenja bilo kakvih komponenata.
run(new IzgledIPonasanje()» 300, 300);
}
} ///:-

Za eksplicitno zađavanje izgleda i ponašanja 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 oponašaju izgled i ponašanje W indow sa i M acOS-a, one se
m ogu koristiti sam o na odgovarajućim platform am a (dobijaju se kada pozovete m etodu
getSystem L ookA ndF eeIC lassN am e() dok radite na određenoj platform i).
M oguće je napraviti i sopstveni paket elem enata koji opisuju izgled i ponašanje, na
prim er ako pravite strukturu aplikacije za kom paniju koja želi da ta aplikacija posebno iz-
gleda. To je velik posao i uveliko premašuje tem u ove knjige (zapravo, otkrićete da se
njom e ne bave ni m noge knjige isključivo posvećene biblioteci Swing).

Stabla, tabele i clipboard


Kratak uvod i prim ere za ove tem e naći ćete u m režnim dodacim a ovog poglavlja na adre-
si w w w .M ind V iew .n et.

JN LP i Java Web Start


Aplet m ože biti potpisan da bi se ostvarila bezbednost. To je pokazano u m režnom doda-
tku ovog poglavlja na adresi w w w .M indV iew .net. Potpisani apleti su m oćn i i m ogu da za-
m ene aplikacije, ali moraju da se izvršavaju u čitaču Weba. To znači da čitač koji se
izvršava na korisnikovom računaru prouzrokuje dodatne režijske troškove a korisničko
okruženje apleta ograničeno 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 režno pokretanje (Java N etw ork Launch Protocol, JNLP) rešava pro-
blem a da ne ugrožava prednosti apleta. JNLP aplikacija m ože se preuzeti i instalirati kao
sam ostalna Java aplikacija na računaru korisnika. Ona se m ože pokretati s kom andne lini-

Ovaj odeljak n apisao je Jeremy Meyer.


1106 Misliti na Javi

je, pom oću ikonice na radnoj površini ili programa za upravljanje aplikacijama koji je deo
realizacije JNLP-a. Aplikacija se m ože pokretati čak i s Web lokacije s koje je bila preuzeta.
U vrem e izvršavanja, JNLP aplikacija m ože dinam ički da preuzim a resurse sa Interne-
ta i da autom atski proverava verziju ako je korisnik povezan sa Internetom . To znači da
im a sve prednosti apleta, a i prednosti sam ostalnih aplikacija.
Korisnikov računar mora oprezno da tretira JNLP aplikacije, kao i aplete. Zato se
JNLP aplikacije izvršavaju sam o u „bazenu s peskom “, kao apleti. U koliko su u potpisa-
nim JAR datotekama, korisnik m ože odlučiti da im ukaže poverenje i dopusti korišćenje
resursa njegovog računara. (Isto važi i za aplete.) Za razliku od apleta, p o m o ću usluga
JNLP interfejsa za programiranje JNLP aplikacije m ogu zatražiti pristup određenim re-
sursima korisnikovog računara i kada su u nepotpisanim JAR datotekam a. Tokom izvrša-
vanja programa, od korisnika se traži da odobri te zahteve.
JNLP je specifikacija protokola, a ne njegova realizacija. Zato je za korišćenje neop-
h odna i neka realizacija. Java Web Start (JAWS) Sunova je besplatna zvanična referentna
realizacija koja se isporučuje u sklopu Jave SE5. U koliko želite da je koristite za razvoj,
njena JAR datoteka (javaws.jar) mora biti u putanji klasa vašeg računara; najjednostav-
nije rešenje je dodati datoteku javaw s.jar putanji klasa —prem estiti je iz njene uobičajene
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). Odgovarajuće pojedinosti pročitajte u korisničkim uputstvim a za svoj server.
Nije teško napraviti JNLP aplikaciju. Napravite običnu aplikaciju, kom prim ujte je u
JAR arhivu i zatim obezbedite datoteku za pokretanje - jednostavnu XML datoteku koja
korisnikovom računaru daje sve informacije potrebne za preuzim anje i instaliranje te ap-
likacije. U koliko ne potpišete JAR arhivu, morate da koristite usluge koje interfejs za pro-
gramiranje JNLP aplikacija pruža za sve tipove resursa kojima aplikacija treba da pristupi
na računaru korisnika.
Sledi varijanta programa TestBiranjaD atoteka.java koja za otvaranje birača datoteka
upotrebljava JNI.P usluge, čim e om ogućuje da klasa bude distribuirana u obliku JNLP
aplikacije u nepotpisanoj JAR arhivi.

//: gui/jnlp/JnlpBiranjeDatoteka.java
// Otvaranje datoteka na lokalnom računaru pomoću 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: Grafička korisnička okruženja 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);
ađd(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 pažnju 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 zatražene m etod om ServiceM anager.Iookup( ), a resur-
sim a na računaru korisnika m ože se pristupiti sam o preko objekata koje ta m etoda vrati.
U ovom slučaju, u sistem u datoteka korisnikovog računara datoteke se čitaju i upisuju
p o m oću interfejsa FileContent, koji obezbeđuje JNI.P. Svaki pokušaj neposrednog pri-
stupanja resursima preko, recim o, objekta tipa File ili FileReader, izazvao bi bacanje izu-
zetka SecurityException, jednako kao da ih pokušate upotrebiti iz nepotpisanog apieta.
U koliko želite da koristite te klase i da ne budete ograničeni na interfejse JNLP usluga,
m orate potpisati JAR arhivu.
Potrebnu JAR arhivu pravi kom anda jar koja je na početku programa JnlpBiranjeDa-
toteka.java pretvorena u komentar. Ovo je datoteka za pokretanje prethodnog primera.
Poglavlje 22: Grafička korisnička okruženja 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 naći ćete u datoteci izvornog koda ove knjige (m ože
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-više jasnih
po sebi.
Atribut spec elem enta jnlp saopštava klijentskom sistem u koja verzija JNLP-a m ože da
pokrene aplikaciju. Atribut codebase pokazuje na URL adresu na kojoj su resursi i dato-
teka za pokretanje. Ovde pokazuje na direktorijum lokalnog računara, što je dobar način
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 odgovarajući direktorijum vašeg rac'unara —tek tada će seprogram učitati. Atri-
but href specificira im e datoteke koju treba učitati.
Oznaka inform ation im a razne p od elem ente koji pružaju 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 prečice itd.
Svrha oznake resources slićna 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 učita celu
arhivu ili ne mora.
1110 Misliti na Javi

Atribut application-desc saopštava realizaciji JNLP-a koja klasa je izvršna klasa (ulaz-
na tačka) 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 oću
JNLP usluga.
Pojedinosti o ostalim oznakama potražite 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 sadrži 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>
///: -

Pošto preuzm ete aplikaciju, m ožete da je konfigurišete p o m o ću adm inistrativne kon-


zole. Ako Java Web Start koristite u W indow su, prilikom druge upotrebe m oći ćete da na-
pravite prečicu do aplikacije. To ponašanje se m ože konfigurisati.
O vde sam opisao sam o dve JNLP usluge, a u tekućem izđanju ih im a sedam . Svakom
od njih obavlja se određeni zadatak, kao što je štam panje ili isecanje i prenošenje na clip-
board. Više informacija o tom e potražite na adresi http://ja va .su n .co m .

Paralelno izvršavanje i Svving


Kada programirate u Swingu, radite s nitim a. To ste videli na početku 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 raspoređivača. M eđutim , činjenica da ne m orate eksplicitno praviti niti (objekte
klase T hread) znači da bi problem i s nitim a m ogli da vas snađu kada se ne nadate. Vodite
računa o tom e da nit Sw ingovog raspoređivača uvek postoji i da sve Swing događaje
obraduje vadeći ih iz reda za čekanje i izvršavajući ih jedan za drugim . Ukoliko ne zabora-
vite na nit raspoređivača, manje su šanse da će vaša aplikacija upasti u uzajamnu blokadu
ili uslov za trku.
U narednom odeljku razm otrićem o oblasti višen itn og izvršavanja na koje treba obra-
titi pažnju prilikom rada u Swingu.
Poglavlje 22: Grafička korisnička okruženja 1111

Dugotrajni zadaci
Jedna od osnovnih grešaka pri programiranju grafičkih korisničkih okruženja jeste upo-
treba rasporedivača za izvršavanje dugotrajnog zadatka. Sledi jednostavan primer:

//: gui/DugotrajanZadatak.java
// Loše 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 završen");
}
});
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 raspoređivača iznenada počinje da izvršava dugotrajan zadatak.


Videcete da ni to dugm e nece stiči da se vrati iz utisnutog položaja, pošto je mt raspoređi-
vača, koja bi inače osvežila sliku na ekranu, zauzeta tokoin cele 3 sekunde. A i ništa drugo
ne m ožete da uradite, npr. da pritisnete d2, pošto program ne odgovara dok se zadatak
dugm eta d l ne završi i nit raspoređivača opet ne postane dostupna. Kod u d2 je pogrešan
pokušaj da se problem reši prekidanjem niti raspoređivača.
1112 Misliti na Javi

Naravno, rešenje je da se dugotrajni procesi izvršavaju u zasebnim nitim a. Sada ćem o


upotrebiti jed n on itn i izvršilac (Executor, koji pozivajuće zadatke autom atski ubacuje u
red za čekanje. Odatle ih vadi i izvršava 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 + " završen1');
}
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: Grafička korisnička okruženja 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 pokušate da dodate još zadataka, do-
bićete izuzetak. Dakle, pritisak na d2 ćini da program umre. Želeli sm o da ugasim o tekući
zadatak (i otkažem 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 izvršavanje. D efi-
nisaćem o novu klasu MenadzerZadataka; ona sadrži 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 ogućuje da pratim o prvobitni zadatak, kako bism o im ali i d o-
datne inform acije kojima Future ne raspolaže. 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, pošto 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 opšte namene:

//: net/mindview/util/MenadzerZadataka.java
// Upravljanje redom zadataka i njegovo izvršavanje.
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 završene zadatke radi izveštavanja o rezultatima:
if (Istavka.buduci .isDoneO) {
rezultati,add("Otkazujem " + stavka.zadatak);
stavka.buduci.cancel(true); // Može da prekine
stavke.remove();
}
}
return rezultati;
}
} III--
MenadzerZadataka je lista tipa ArrayList čije su stavke tipa StavkaZadatka. On sadrži
i jednonitni Executor, pa kada pozovete m etodu a d d ( ) i prosledite joj objekat tipa Callable,
ona šalje taj Callable, a rezultujući objekat tipa Future skladišti zajedno s prvobitnim zadat-
kom. Na taj način, ako išta bude trebalo da se radi s tim zadatkom, imaćete 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 našem primeru:

// : gui/DugotrCallableKoj1SeMozePrekinuti.java
// Korišćenje Callable objekata za dugotrajne zadatke.
import javax.swing.*;
import java.awt.*;
Poglav[je 22: Grafička korisnička okruženja 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 vraća i rezultat - u
ovom slučaju String identifikator zadatka.
Za rešavanje istog problem a napisane su i n e-Sw ing uslužne klase (koje se ne isporuču-
ju u standardnoj distribuciji Jave). To su SwingWorker (naći ć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 izvršava i oba-
vestiti ga dokle je odm aklo izvršavanje. To se ob ičn o radi p om o ću klasa JProgressBar ili
ProgressMonitor. U narednom prim eru upotrebiću ProgressMonitor:

//: gui/NadziranDugotrCal 1 able.java


// Prikazivanje napredovanja zadatka pomoću 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: Grafička korisnička okruženja 11 17

}
}
);
}
} catch(InterruptedException e) {
monitor.close();
System.out.println(this + " prekinut");
return "Rezultat: " + this + " prekinut";
}
System.out.println(this + " završen");
return "Rezultat: " + this + " završen";
}
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( ) ažurira ProgressM onitor svako pola sekunde. Imajte u
vidu da je NadziranCallable zaseban zadatak i da ne bi trebalo da neposredno kontroliše
ulazno/izlazne operacije, pa sam m onitoru m etod om SwingUtilities.invokeLater( )
podneo informacije o prom eni stepena napredovanja. U Sunovom udžbeniku za Swing
(„Swing Tutorial" na adresi http://java.sun.com ) prikazan je drugačiji pristup; tam o se
upotrebljava Swing Timer, koji proverava status zadatka i ažurira m onitor.
Ako se na m onitoru pritisne dugm e cancel, m etoda m onitor.isC anceled( ) vraća 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.
Vežba 33; (6) Izmenite DugotrCallableKojiSeMozePrekinuti.java tako da sve zadatke
izvršava paralelno, a ne sekvencijalno.

Vizuelno višenitno programiranje


U narednom primeru napravićem o Runnable objekat tipa IPanel (pano) koji sebe boji
raznim bojama. Aplikacija je podešena tako da s kom andne linije prim a parametre koji
određuju veličinu tabele boja i - m etod om sle e p ( ) —trajanje spavanja izm eđu prom ena
boja. Poigrajte se s tim parametrima i m ogli biste otkriti zanim ljiva i m ožda neobjašnjiva
obeležja višenitnog izvršavanja na vašoj platformi:

//: gui/ObojeneKutije.java
// Vizuelni prikaz višenitnog izvršavanja.
// {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: Grafička korisnička okruženja 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 način 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 konfiguriše objekat tipa GridLayout tako da u svakoj dim enziji ima
brojcelija. Zatim dodaje odgovarajući broj ObKut objekata da popuni taj broj ćelija i sva-
kom prosleđuje broj pauza. U m etodi m a in ( ) vidite da pauza i brojcelija imaju podrazu-
m evane vrednosti koje se m ogu menjati prosleđivanjem 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 ože biti nezavisan zadatak. Tim zadacim a upravlja
grupa niti ExecutorService.
1120 Misliti na Javi

Tekuća boja ćelije je boja. Boje pravi konstruktor klase Color čiji ulazni argum ent tre-
ba da bude 24-bitni broj koji u ovom slučaju pravim o nasum ično.
M etoda paintC om ponent( ) sasvim je jednostavna; ona sam o menja boju kako joj
kaže boja i ceo pano (objekat tipa JPanel) popunjava njom .
U m etodi r u n ( ) postoji beskonačna petlja koja objektu boja zadaje novu nasum ično
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 ( ) zaslužuje pažnju. Na prvi pogled izgle-
da da pravim o m nogo niti i da se sve m oraju prebrojiti. M ožda vam izgleda da tim e krši-
m o princip na osnovu koga sve zadatke treba poslati raspoređivaču, od n osn o redu za
čekanje. M eđutim , 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 određeni indikator
,,zamrljano“, kojim pokazuje da je ta oblast kandidat za prebojavanje kada raspoređivač
sledeći put bude sprem an za tu akciju. Zato program ne prouzrokuje problem e sa Swin-
govim nitima.
Kada nit raspoređivača događaja 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 redefinišete 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 ožete da eksperim entišete i pravite proizvoljan broj niti. (U praksi, ograničenje nam eće
broj niti s kojima vaša JVM m ože da izađe na kraj.)
Ovaj program om ogućuje zanim ljivo poređenje perform ansi, pošto m ože da pokaže
velike razlike u ponašanju i perform ansam a raznih realizacija JVM niti i raznih platformi.
Vežba 34: (4) Izm enite program ObojeneKutije.java tako da prvo nasum ično prska tač-
ke (,,zvezde“) po platnu, a zatim nasum ično menja boje tih ,,zvezda“.

Vizuelno programiranje i zrna Jave


D osad ste u ovoj knjizi videli koliko je Java korisna za pisanje višekratno upotrebljivog
koda. Jedinica koda koja se najčešće m ože p on ovo iskoristiti jeste klasa jer se sastoji od ka-
rakteristika (polja) i ponašanja (m etoda) koje se niogu pon ovo koristiti - kom pozicijom
ili nasleđivanjem.
Nasledivanje i polim orfizam su osn ovn i principi objektno orijentisanog programira-
nja, ali kada pišete aplikaciju, najčešće se trudite da kom ponente rade taćno on o što vi ho-
ćete. Zeleli biste da ubacite te delove u svoj program kao što elektroinženjer postavlja
čipove na ploču. Takođe, izgleda da bi trebalo da postoji neki način 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 najveća inspiracija za zrna Jave. U tim progra-
m erskim alatkama kom ponente su predstavljene vizuelno, što ima sm isla pošto one obič-
no prikazuju neki grafički elem ent, kao što su dugm e ili polje za tekst. Zapravo, takva
Poglav[je 22: Grafička korisnička okruženja 1121

vizuelna predstava je često izgled kom ponente u program u koji se izvršava. Zbog toga
deo postupka vizuelnog programiranja podrazum eva prevlačenje kom ponente s palete i
spuštanje na obrazac. Alatka za pravljenje aplikacija piše kod dok vi prevlačite kom p o-
nente, a taj kod prouzrokuje pravljenje kom ponente u izvršnom programu.
Sam o prevlačenje kom ponente u obrazac obično nije dovoljno za kom pletiranje pro-
grama. Č esto m orate da prom enite neka njena obeležja, npr. boju, tekst koji sadrži, bazu
podataka s kojom je povezana itd. Obeležja koja se m ogu prom eniti tokom projektovanja
označavaju se kao svojstva (engl. properties). Sa svojstvim a kom ponente m ožete 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 ponašanje.
Tokom projektovanja, ponašanje vizuelne kom ponente je delim ično predstavljeno doga-
đajim a. Događaj znači: „Evo nečega što bi se m oglo dogod iti kom p on en ti“. O bično od lu -
čujete šta treba da se desi nakon događaja tako što povezujete kod sa određenim
događajem .
Evo izuzetno važnog dela: alatka za pravljenje aplikacija koristi refleksiju za dinam ičko
ispitivanje k om p on ente i otkrivanje svojstava i događaja koja kom ponenta podržava.
Kada to sazna, m ože da prikaže svojstva i om ogu ći đa ih m enjate (uz snim anje stanja), ali
m ože i da prikaže događaje. Po pravilu, vi dvaput pritiskate m išem , a alatka za pravljenje
aplikacija pravi kod i povezuje ga s tim događajem . U tom trenutku treba sam o da na-
pišete kod koji se izvršava kada se desi događaj.
Dakle, alatka za pravljenje aplikacija obavlja najveći deo posla um esto vas. Zbog toga
se m ožete 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 važi za korisnič-
ka okruženja, ali često i za druge delove aplikacije.

Šta je zrno?
Kada se prašina slegne, vidi se da je kom ponenta sam o blok koda koji je ob ičn o predsta-
vljen u obliku klase. Ključno pitanjeje m ogućnost alatke za pravljenje aplikacija da otkri-
va svojstva i događaje te kom ponente. Da bi napravio VB kom ponentu, programer je
m orao da piše prilično složen kod u kom e je poštovao određena pravila za otkrivanje
svojstava i događaja. Delphi je bila alatka za vizuelno programiranje druge generacije. Taj
jezik je aktivno koristio vizuelno programiranje pa se vizuelna kom ponenta m n ogo lakše
pravila. M edutim , izradu vizuelnih kom ponenata na najviši nivo dovela je Java, i to p o-
m oću zrna (engl. Java BcansJ, pošto je zrno sam o klasa. Ne m orate pisati nikakav dodatni
kod niti koristiti specijalna proširenja jezika da bi nešto postalo zrno. Zapravo, treba
sam o m alo dn prom enite način im enovanja metoda. Na osn ovu imena m etode, alatka za
pravljenje aplikacija zna da li se radi o svojstvu, dogadaju ili običnoj m etodi.
U dokum entaciji za Javu ova pravila im enovanja pogrešno su označena kao „projektni
obrazac“. To je nesporazum , pošto su projektni obrasci dovoljan izazov i bez ove zbrke
(naći ćete ih u knjizi T h in kin g in Patterns w ith Java koju m ožete 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 prilično jednostavnim :
1 . Da bi se neko svojstvo nazvalo xxx, ob ičn o treba napraviti dve metode: getX xx() i
setX x x ( ). Obratite pažnju 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 vraća m etoda get isti je kao tip argu-
m enta m etode set. Im e svojstva i tip ovi koje vraćaju m etode get i set ne moraju da
b udu povezani.
2. Za svojstvo tipa boolean m ožete da koristite prethodni pristup s m etodam a get i
set, ali um esto get m ožete da koristite is.
3. O bične m etode zrna ne poštuju prethodna pravila za dodeljivanje im ena, ali su jav-
ne.
4. Za događaje 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) obrađuje se događaj BounceEvent. Najčešće će ugrađe-
ni događaji i prijem nici zadovoljavati vaše potrebe, ali m ožete da napravite i
sopstvene događaje i prijem ničke interfejse.
Ova saznanja m ožem o da iskoristim o za pravljenje jednostavnog zrna:

//: frogbean:Frog.java
// Obično 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: Grafička korisnička okruženja 1123

public void addActionListener(ActionListener 1) {


II...
}
public void removeActionListener(ActionListener 1) {
//
}
public void addKeyListener(KeyListener 1) {
/ / ■••
}
public void removeKeyListener(KeyListener 1) {
//
}
// "Obična" javna metoda:
public void croak() {
System.out.println("Ribbet!");
}
} ///:-

Prvo uočavate da je ovo obična klasa. Sva njena polja ob ićn o će biti privatna i m oći ć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 pažnju na prom enu veličine prvog slova im ena
svojstva). Iako je im e lokalnog identifikatora isto kao im e svojstva u prva tri slučaja, svoj-
stvo jum per pokazuje da im e svojstva nije uslov za korišćenje tačno određenog identifi-
katora za lokalne prom enljive. (Lokalne prom enljive za to svojstvo čak ne moraju ni da
postoje.)
Događaji koje generiše ovo zrno su ActionEvent i KeyEvent, što zaključujemo po
im enim a m etoda add i rem ove za rad s prijem nicim a. Konačno, vidite da je obična m e-
toda cro a k ( ) i dalje deo zrna sam o zato što je javna, a ne zato što poštuje pravilo dodelji-
vanja im ena.

Ispitivanje zrna klasom Introspector


Jedna od najosetljivijih strana zrna uočava se kada zrno prevlačite s palete i spuštate na
obrazac. Alatka za pravljenje aplikacije mora biti u stanju da napravi zrno (što m ože 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 događaja.
D eo rešenja ćete naći na kraju poglavlja Podaci o tipu: favina refleksija om ogućuje da se
otkriju sve m etode bilo koje klase. To je savršeno za rešavanje problem a sa zrnim a bez ko-
rišćenja dodatnih rezervisanih reči jezika, što je potrebno u drugim jezicim a za vizuelno
programiranje. Jedan od najvažnijih razloga za dodavanje refleksije u Javu i jeste bilo po-
državanje zrna (mada refleksija om ogućuje i seriializaciju objekata i daljinsko pozivanje
m etod a). Dakle, m ože se očekivati 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 oguće, ali su projektanti Jave želeli da obezbede standardnu alatku, ne


sam o da bi se zrna lakše koristila, već i da bi se obezbedio standardizovan način za pra-
vljenje složenijih zrna. Ta alatka je Idasa Introspector, a njena najvažnija m etoda je static
getB eanInfo( ). Ovoj m etodi se prosleđuje referenca na objekat ldase Class, a ona iscrpno
ispituje klasu i vraća objekat tipa Beanlnfo koji p otom m ožete analizirati da biste saznali
svojstva, m etode i događaje zrna.
O b ično vam ništa od toga neće biti važno; najveći broj zrna koristićete gotova i nema
potrebe da znate detalje koji se dešavaju unutar njih. Sam o ćete prevlačiti zrna na obrazac,
zatim podešavati njihova svojstva i pisati procedure za događaje koji vas zanim aju. M e-
đutim , vežba korišćenja klase Introspector za prikazivanje inform acija o zrnu zanimljiva
je i poučna, 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: Grafička korisnička okruženja 1125

print("Javne metode:");
for(MethodDescriptor m : bi .getMethodDescriptorsO)
print(m.getMethod() .toSt r i n g O ) ;
print("======================");
print("Podrška za događaje:");
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 pronađem" + 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 pokušava da napravi


objekat interfejsa Beanlnfo, a ako uspe, poziva m etod e koje vraćaju inform acije o svoj-
stvim a zrna, m etodam a i događajima. U m etodi Introspector.getBeanInfo( ) prim etićete
drugi argum ent. O n kaže objektu klase gde da se zaustavi u hijerarhiji nasleđivanja. Ovde
se zaustavlja pre nego što analizira m etode klase Object, pošto nas on e ne interesuju.
M etoda getPropertyD escriptors( ) vraća niz objekata tipa PropertyDescriptor koji
opisuju svojstva. Za svaki deskriptor svojstava (objekat tipa PropertyDescriptor) m ožete
da pozovete m etodu getPropertyType( ) da biste otkrili klasu objekta koji se prosleđuje
unutra i napolje p om oću m etoda svojstava. Zatim, za svako svojstvo m ožete 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 oću getR eadM ethod( ) i m etodu za upisivanje p om oću getW riteMethod(
). D ve poslednje m etode vraćaju objekat tipa M ethod koji se m ože koristiti za pozivanje
odgovarajuće m etode objekta (to je deo refleksije).
Što se tiče javnih m etoda (uključujući i m etode svojstava), getM ethodD escriptors( )
vraća niz objekata klase M ethodDescriptor. M ožete da dobijete objekat tipa M ethod p o-
vezan sa svakim od tih objekata i da ispišete njegovo ime.
Što se tiče događaja, m etoda getEventSetDescriptors( ) vraća niz (čega drugog nego)
objekata tipa EventSetDescriptor. Svaki od njih se m ože ispitati da bi se otkrila klasa pri-
jem nika, m etode prijem ničke klase, kao i m etode za dodavanje i uklanjanje prijemnika.
Program IspitivanjeZrna ispisuje sve ove informacije.
Pre izvršavanja, 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: Grafička korisnička okruženja 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)

Podrška za događaje:
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 najveći d eo svojstava koje vidi klasa Introspector dok na osno -
vu zrna pravi objekat tipa Beanlnfo. Prim etićete da su tip svojstva i njegovo im e nezavi-
sni. Obratite pažnju na to da su sva slova u im enu svojstva mala. (Jedino se odstupa kada
im e svojstva poćinje 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 ože koristiti za pozivanje odgovarajuće m etode.
Spisak javnih m etoda sadrži m etod e koje nisu povezane s nekim svojstvom ili događa-
jem , kakva je m etoda croak ( ), ali i o n e koje jesu povezane sa svojstvima i događajima. To
su sve m etode zrna koje m ožete da pozovete iz programa, a alatka za pravljenje aplikacija
m ože ih sve prikazati dok pišete program da bi vam olakšala posao.
Konačno, prim ećuje se da su događaji 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 ožete da saznate sve što je važno o zrnu. M ožete 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
Sledeći prim er je sam o nešto m alo napredniji, mada je frivolan. To je objekat klase JPanel
koji iscrtava mali krug oko miša kad god se on pom eri. Kada pritisnete taster miša, reč
Bang! se pojavljuje u sredini ekrana i pokreće se prijem nik događaja.
M ožete da prom enite sledeća svojstva: veličinu kruga, boju, veličinu i reč koja se pri-
kazuje kada se pritisne taster miša. Objekat BangBean takode ima sopstvene m etode ad-
dActionListener( ) i rem oveA ctionListener( ) p om oću kojih m ožete da povežete zrno sa
svojim prijem nikom koji se pokreće kada korisnik pritisne m išem . Sada bi već trebalo da
um ete prepoznati podršku za svojstva i događaje:

//: bangbean:BangBean.java
// Grafičko zrno.
package bangbean;
import javax. swi ng
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.uti1.*;
Poglavlje 22: Grafička korisnička okruženja 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 prijemničku 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 znači da alat-
ka za pravljenje aplikacija m ože da učita sve inform acije o zrnu p om oću serijalizacije, na-
kon što projektant programa podesi vrednosti svojstava. Kađa se zrno napravi kao deo
izvršne aplikacije, snim ljena svojstva se rekonstruišu pa se dobija tačno on o što ste pro-
jektovali.
U potpisu m etode addA ctionListener( ) vidite da ona m ože da generiše izuzetak To-
oManyListenersException. To znači da je događaj jcdnosm eran, tj. da se sam o jedan pri-
jem nik obaveštava o tom e da se desio. O b ičn o ćete koristiti višesm erne događaje tako da
o događaju bude obavešteno više prijem nika. M eđutim , ovde već uiazim o u tem e koje će
biti razm otrene u odeljku „Zrna Jave i sinhronizacija“. Jednosm ernim događajem zasad
ćem o zaobići ovaj problem .
Kada pritisnete taster miša, 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: Grafička korisnička okruženja 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("Previše prijemnika");
}
add(bb);
add(BorderLayout.SOUTH, txt);
}
public static void main(String[] args) {
run(new BangBeanTest(), 400, 500);
}
1 ///:-

Ova klasa se neće koristiti kada se zrno nalazi u razvojnom okruženju, ali je korisna
kao način za brzo testiranje svih zrna. BangBeanTest stavlja zrno BangBean u aplet, po-
vezujući s njini jednostavan prijem nik tipa ActionListener koji ispisuje broj đogađaja u
tekstualnom polju kad god se desi događaj. Alatka za izradu aplikacija obično pravi naj-
veći deo koda koji koristi zrno.
Kada ispitate ovo zrno Idasom IspitivanjeZrna ili ga stavite u razvojno okruženje koje
podržava zrna, prim etićete da ima m n ogo više 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 takođe
zrno, pa se prikazuju i njegova svojstva i dogadaji.
Vežba 35: (6) Pronadite na Internetu i preuzm ite jednu ili više besplatnih vizuelnih alatki
za pravljenje grafičkih korisničkih okruženja, ili kupite neku. Saznajte šta je neophodno
da bi se zrno BangBean unelo i koristilo u tom okruženju.

Zrna Jave i sinhronizacija


Kad god napravite zrno, treba da očekujete da će o n o raditi u višenitnom okruženju. To
znači sledeće:
1. Kad god je m oguće, sve javnc m etode jednog zrna treba sinhronizovati. Naravno,
tim e izazivate povećane procesorske troškove koje povlači atribut synchronized.
Ako su oni neprihvatljivi, m etode koje neće izazvati problem e u kritičnim delovim a
programa m ogu da ostanu nesinhronizovane, ali imajte u vidu da to nije uvek lako
proceniti. O b ičn o su kandidati za to male m etode (p opu t m etode getCircleSize() u
sledećem prim eru) i (ili) ,,atom ične“, pa se m etoda izvršava u tako kratkom vre-
m enu da objekat ne m ože da bude izm enjen tokom izvršavanja. Ako takve m etode
1 132 Misliti na Javi

ostanu nesinhronizovane, izvršavanje celog programa m ožda se neće značajno


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 značajno
ubrzati izvršavanje programa.
2 . Prilikom obaveštavanja više prijemnika (engl. m ultica st ) o nekom događaju, m ora-
te pretpostaviti da tokom kretanja kroz taj spisak neki prijem nici m ogu biti dodati,
a drugi uklonjeni.
Prvu tačku je lako uraditi, ali pri izvođenju druge treba m alo razmisliti. U zm im o pri-
m er zrna BangBean.java predstavljenog u prošlom poglavlju. Tamo je izbegnuto pitanje
korišćenja više niti tako što je zanemarena rezervisana reč sy n ch ro n ized i obavešten sam o
jedan prijem nik (engl. unicast). Evo tog prim era, izm enjenog tako da koristi više niti i da
o događajima obaveštava više prijemnika:

//: gui/BangBean2.java
// Trebalo bi da svoja zrna ovako pišete,
// kako bi mogla da se izvršavaju u okruženju s više 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; // veličina kružnice
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: Grafička korisnička okruženja 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 više prijemnika što se češče koristi
// od obaveštavanja samo jednog prijemnika,
// kako je bilo urađeno 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 površnu kopiju spiska za slučaj
// 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.drđwString(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 reči synchronized m etodam a jeste mala i laka izm ena. M eđu-
tim , uočite 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 više prijemnika.
Vidi se da m etoda notifyL isteners( ) nije sinhronizovana. Nju m ože istovrem eno da
pozove više niti. M oguće 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 ublažio, spisak je unutar sinhro-
nizovanog dela koda kloniran p om oću konstruktora spiska prijem nika ArrayList koji
kopira elem ente svog argumenta, pa se prolazi kroz klon. Tako se m ože raditi sa original-
nim spiskom prijemnika, a da se pri tom ne utiče na tnetodu notifyListeners( ).
N i m etoda paintComponent( ) nije sinhronizovana. Odiučiti 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 sledeća pitanja:
Poglavlje 22: Grafička korisnička okruženja 1135

T. D a li ta m etoda menja stanje kritičnih prom enljivih unutar objekta? D a biste otkrili
koje su prom enljive kritične, m orate utvrditi da li će ih druge niti u program u čitati
ili im zadavati vrednosti. (U ovom slučaju, čitanje i zadavanje gotovo se uvek oba-
vlja unutar sinhronizovanih m etoda, pa m ožete da ispitate sam o njih.) M etoda
paintCom ponentO nem a takvih izm ena stanja.
2 . Da li m etoda zavisi od stanja tih kritičnih promenljivih? Ako neka sinhronizovana
m etoda m odifikuje prom enljivu koju vaša m etoda koristi, trebalo bi da i nju
sinhronizujete. Na osnovu toga m ogli biste uočiti da prom enljivu cSize m odifikuju
sinhronizovane m etode, pa bi stoga m etodu paintC om ponent( ) trebalo sinhroni-
zovati. U ovom slučaju, m eđutim , m ožete da se zapitate: „Šta se najgore m ože d o-
goditi ako se sadržaj prom enljive cSize prom eni tokom izvršavanja m etode
p aintC om p onent( )?“ Ako zaključite da to što se m ože desiti nije tako strašno, a uz
to je i prolazno, m ožete da ostavite m etodu p aintC om ponent( ) nesinhronizovanu
kako biste izbegli dodatne režijske troškove 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 razmišljanje. U ovom slučaju, 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 eđutim , da se atribut
synchronized ne nasleđuje - znači, 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 najbrže m oguće. Izuzetno se
preporučuje korišćenje svega što iz njih uklanja režijske troškove, pa ako smatrate
da te m etode treba da sinhronizujete, to m ože biti znak lošeg projekta.
Kod za testiranje unutar m etode m a in ( ) drugačiji je nego onaj u program u BangBe-
anTest, kako bi se pokazala sposobnost klase BangBean2 da o dogadajim a obaveštava
više 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 sadrži sve klase zrna i m anifest datoteku. M anifest datoteka je tek-
stualna datoteka određenog 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 obaveštenja iz kom pa-
nije Sun, iznosi 1.0. Drugi red (prazni redovi se zanemaruju) navodi datoteku Bang-
Bean.class, a treći kaže ,,To je zrno“. Bez trećeg 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, videćete 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 sadrži informacije o paketu. O sim toga, m anifest datoteku m orate da sm e-
stite u direktorijum iznad korena putanje paketa, što u ovom slučaju znači 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 ožda se pitate šta se dešava sa svim drugim klasama koje se generišu prilikom pre-
vođenja programa BangBean.java. O n e završavaju u poddirektorijum u bangbean, a pri-
m etićete 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 (uključujući, u ovom slučaju, i originalnu datoteku sa izvornim ko-
dom programa BangBean.java; vi ćete m ožda odlučiti da izvorni koti ne pakujete zajed-
no sa zrnim a). Pored toga, ako otpakujete JAR arhivu koja je upravo napravljena,
otkrićete da se m anifest datoteka ne nalazi u paketu, već da je program jar napravio sop-
stvenu datoteku koja se delim ično zasniva na vašoj, a zove se MANIFEST.MF i nalazi se
u poddirektorijum u META-INF (skraćenica za m etainform acije). Kad otvorite tu dato-
teku, prim etićete i da je alatka jar dodala inform acije o digitalnom potpisu svake datoteke
u sledećem obliku:

Digest-Algorithms: SHA MD5


SHA-Digest: pDpEAG9Naecx8aFtqPI4udsx/00=
MD5-Digest: 04NcSlhE3Smnzlp2Hhj6qeg==

O ovom e uglavnom ne m orate da vodite računa, a ako nešto menjate, m ožete 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 ožete 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 ožete da arhivirate ceo direktorijum. Vidi se da i Frog i BangBean imaju sopstvene pod-
direktorijume.
Kada pravilno sm estite zrno u arhivu, m ožete da ga unesete u alatku za izradu progra-
ma koja podržava zrna. Način na koji se to radi razlikuje se od alatke do alatke, ali kom-
panija Sun obezbeđuje besplatno okruženje za testiranje Javinih zrna koje se zove Bean
Builder. (M ožete 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 odgovarajući poddirektorijum .
Vežba 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 sadrži zrna
Frog i BangBean. Zatim sa Interneta preuzm ite i instalirajte Bean Builder ili upotrebite
svoju alatku za pravljenje programa koja podržava zrna. Dodajte novu arhivu u to okru-
ženje kako biste m ogli da testirate ta dva zrna.
Poglav[je 22: Grafička korisnička okruženja 1137

Složenija podrška za zrna


Vidite kako je jednostavno napraviti zrno. M eđutim , niste ograničeni sam o na o n o što je
ovde prikazano. Arhitektura Javinih zrna obezbeđuje lak početak, ali m ože da posluži i u
složenijim situacijama koje prevazilaze okvire ove knjige i biće ukratko izložene. Više de-
talja saznaćete na lokaciji java.sun.com .
Svojstva zrna m ogu da se poboljšaju. U prethodnim prim erim a prikazana su sam o p o-
jedinačna svojstva, ali je m oguće predstaviti i više svojstava u obliku niza. To se zove indek-
sirano svojstvo. Vi obezbeđujete sam o odgovarajuće m etode (naravno, poštujući pravila za
dodeljivanje im ena m etodam a), a objekat klase Introspector prepoznaje indeksirano svoj-
stvo tako da alatka za pravljenje aplikacija m ože da reaguje na odgovarajući način.
Svojstva m ogu da budu povezana (engl. bound), što znači da obaveštavaju druge
objekte o prom enam a p o m o ću događaja tipa PropertyChangeEvent. D rugi objekti po-
tom m ogu da odluče da li će se prom eniti na osn ovu prom ene zrna.
Svojstva m ogu da budu ograničena (engl. constrained), što znači da drugi objekti m ogu
da zabrane prom enu nekog svojstva ako je neprihvatljiva. D rugi objekti se obaveštavaju
po m oću događaja tipa PropertyChangeEvent, a m ogu da generišu izuzetak tipa Pro-
pertyVetoException kako bi sprečili prom enu i vratili stare vrednosti.
M oguće je menjati i način predstavljanja zrna tokom projektovanja:
1. M ožete da obezbedite nam enski spisak svojstava za određeno zrno. O bičan spisak
svojstava koristiće se za sva ostala zrna, ali će se vaš spisak autom atski pozivati kada
se izabere vaše zrno.
2 . M ožete da napravite nam ensku klasu za prilagođavanje odredenog svojstva (tako
da se koristi običan spisak svojstava, ali kada se menja specijalno svojstvo, auto-
m atski će se pozivati vaša klasa).
3. Za zrno m ožete da obezbedite nam ensku klasu tipa Beanlnfo koja daje informacije
različite od onih koje pravi Introspector.
4. M ožete uključivati i isključivati napredan režim u svim objektima klase Feature-
D escriptor da bi s^razlikovale osn ovn e i složenije m ogućnosti.

Više 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 preporučuje Sun, to nikako ne znači da ne
postoie i d ru g e alatke za pravljenje gralićkih korisnićkih okruženja. Dve važne 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 računarima.
1 138 Misliti na Javi

Zašto biste koristili alternative? Za Web klijente se prilično uverljivo m ože tvrditi da
apleti nisu uspeli. Iako su postojali od početka i m ada se o njim a toliko pričalo i obećava-
lo, danas je iznenađenje kada čovek naiđe 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 prećutno priznanje da
apleti nisu uspeli. Još je važnije to što je Flash Player instaliran na preko 98 procenata
računarskih platformi, pa se m ože sm atrati prihvaćenim standardom . Kao što ćete videti,
sistem Flex obezbeđuje veom a m o ćn o okruženje za programiranje klijentskih aplikacija,
svakako m nogo jače od JavaScripta, a njegov izgled i ponašanje č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 veći i duže 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, pošto se izgled i ponašanje Sw ing aplikacija razlikuje od onih uo-
bičajenih na stonim računarima. Korisnici po pravilu ne žele da svaka aplikacija drugačije
izgleda i ponaša se; oni se trude da što brže završe svoj posao i trude se da sve aplikacije
izgledaju i ponašaju se jednako. SWT pravi aplikacije koje izgledaju kao m atične, a pošto
ta biblioteka koristi m atične kom ponente koliko god je m oguće, te aplikacije se obično
izvršavaju brže od ekvivalentnih Swing aplikacija.

Pravljenje Flash Web klijenata pomoću Flexa


Pošto je laka virtuelna mašina Flash kom panije A dobe široko rasprostranjena, većina
ljudi m ože da bez ikakve instalacije koristi okruženje zasnovano na Flashu, a on o će iz-
gledati i ponašati se jednako na svim sistem im a i p latform am a.10
Za razvoj Flash korisničkih okruženja za Java aplikacije, m ožete 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, slični 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 ički skriptovi se upotrebljavaju za do-
davanje koda za obradu događaja i pozivanje usluga, koji korisničko okruženje povezuje
s Java klasama, m odelim a podataka, Web uslugam a itd. Flexov prevodilac prevodi MXML
i skript datoteke u bajtkod. Flashova virtuelna m ašina na klijentskom računaru radi kao
Javina virtuelna mašina (JVM) utoliko što interpretira prevedeni bajtkod. Format Fla-
shovog bajtkoda nazvan je SWF; SWF datoteke proizvodi Flexov prevodilac.
Vodite računa o tom e da postoji alternativa Flexu na adresi http://openlaszlo.org; njen
izvorni kod je otvoren i strukture slične Flexu, te nekom e m ože bolje poslužiti. Postoje i
druge alatke za pravlienje Flash aplikaciia na različite načine.

1,1 Jezgro m aterijala u ovom odeljku napravio je Sean Neville.


Poglavlje 22: Grafička korisnička okruženja 1139

Zdravo, Flex
Pročitajte ovaj MXML kod, koji definiše korisničko okruženje (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 počinju op isom XML verzije i kodi-
ranja; to su atributi version i encoding. Krajnji spoljašnji MXML elem ent je Application
koji se vizuelno nalazi na vrhu i logički je kontejner za Flex korisničko okruženje. Unutar
elem enta Application rnožete đeklarisati oznake koje predstavljaju vizuelne kontrole kao
što je gornji elem ent Natpis. Kontrole uvek sm eštam 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 slučaju, kao u gornjem prim eru, Ap-
plication deluje kao kontejner. Podrazum evani rasporedivač elem enta Application
smešta kontrole vertikalno niz prozor, redosledom kojim su bile deklarisane.
ActionScript je verzija ECMAScripta, ili JavaScripta, koja p otp u n o liči na Javu i p o-
država klase i strogu proveru tipova, kao i dinam ičko pravljenje skriptova. Kada prim eru
dodam o skript, uvešćem o ponašanje. Ovde MXML kontrola Script sm ešta 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 aveđena adresa više nije do stu p n a . Više inform acija o M X M L-u naći ć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 računa o tom e da je atribut id svake kontrole u skriptu dostupan kao
im e promenljive, pa skript m ože 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).

Prevođenje 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 više izdanja - od besplatnih probnih do serverskih
za velika preduzeća — a za razvoj Flex aplikacija A dobe nudi još neke alatke. Pošto se
izdanja stalno menjaju, pročitajte pojedinosti na Web lokaciji proizvođača Adobe.
Takođe, imajte u vidu da ćete m ožda m orati da m odifikujete datoteku jvm.config u
direktorijum u bin Flexove instalacije.
Za prevođenje MXML koda u Flashov bajtkod im ate dve opcije:
1. M XML datoteku m ožete sm estiti u neku Java Web aplikaciju, pored JSP i HTML
stranica u WAR datoteci, i dati da se u vrem e izvršavanja zahtevi za .m x m l datote-
kom prevode kad god bilo koji čitač zatraži URL adresu tog MXML dokum enta.
2 . MXML datoteku m ožete prevesti p om o ću Flexovog prevodioca m xm lc koji se po-
kreće s kom andne linije.
Prva opcija, prevođenje na W ebu u vrem e izvršavanja, sem Flexa zahteva i neki konte-
jner servleta (kao što je A pacheovT om cat). WAR datotekekontejnera servleta moraju biti
ažurirane 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 ožete sm estiti u Web aplikaciju i zatražiti URL adresu tog dokum enta bilo kojim či-
tačem . Flex će prevesti aplikaciju nakon prvog takvog zahteva, slično kao u m odelu Javi-
nih serverskih stranica (JSP), i p otom će isporučivati prevedeni i keširani 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 ožete upotrebiti kako god želite. Izvršna dato-
teka m xm lc je u direktorijum u b in FIexove instalacije, i kada je pozovete bez argumenata,
ispisaće spisak validnih opcija u kom andnoj liniji. O bično se lokacija Flexove biblioteke
klijentskih kom ponenata zadaje kao vrednost opcije -flexlib, ali u veom a jednostavnim
slučajevim a kao što su dva prethodna, Flexov prevodilac će pretpostaviti gde je biblioteka
kom ponenata. Zato prva dva prim era m ožete 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 ože pokrenuti u Flashu ili sm e-
stiti uz HTM L na neki HTTP server. (N akon učitavanja Flasha u čitač Weba, često je do-
voljno dvaput pritisnuti SWF datoteku da biste je pokrenuli u čitaču.)

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: Grafička korisnička okruženja 1141

Kada pokrenete zdravoflex2.swf, videćete u Flash Playeru sledeće korisničko


okruženje:

jThis vras not too hard to do...|

H*allo1 This was not too hsrd to do.. .

U složenijim aplikacijama m ožete razdvojiti MXML i A ctionScript tako što ćete funk-
cije referencirati u spoljnim ActionScript datotekama. U M XM L-u upotrebite sledeću
sintaksu za kontrolu Script:

<mx:Script source="MojSpoljniSkript.as" />

Taj k od om ogućuje MXML kontrolam a da referenciraju funkcije sm eštene u datoteku


MojSpoljniSkript.as kao da su sm eštene 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 leksićki analizira (raščlanjuje i
razrešava) MXML, prvo transform iše XML u ActionScript i učitava referencirane Ac~
tionScript klase, a potom prevodi i povezuje taj ActionScript u SWF datoteku.
Celu Flex aplikaciju m ožete napisati u sam om A ctionScriptu, a da uopšte ne u po-
trebite MXML. Dakle, MXML nije neophodan, ali je pogodan. MXML se ob ičn o upotre-
bljava za deklarisanje kom ponenata korisničkog okruženja kao što su kontejneri i
kontrole, dok se ActionScript i Java koriste za obradu događaja i ostalu klijentsku logiku.
Imate m ogućnost da pravite sopstvene MXML kontrole i da ih referencirate u Ac-
tionScript klasama. Postojeće MXML kontejnere i kontrole m ožete da kom binujete u no-
vom MXML dokum entu koji m ože biti referenciran kao oznaka u drugim MXML
dokum entim a. Više informacija o tom e potražite 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 eđu kontejnerim a su panoi, vertikalni i ho-
rizontalni pravougaonici, pravougaonici koji se slažu kao pločice, pravougaonici koji se
šire poput harm onike, pravougaonici sa unutrašnjom m režom , tabele itd. Kontrole su
spravice (engl. widgets) korisničkog okruženja kao što su dugm ad, tekstualna polja, kli-
zaći, kalenđari, tabele podataka itd.
Sada ćem o napraviti Flex aplikaciju koja prikazuje i uređuje listu audio datoteka. Vi-
d e ć e t a p r i m e r e kontejnera, kontrola i otkrićete kako đa ih iz Flasha povežete s Javom.
Na početak MXML datoteke postavićem 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 ="0sveži pesme" w M t h = "100"
toolTip=" Osveži listu pesama"
cl ick="uslugaPesama.dajPesmeO" />
</mx:ControlBar>
Poglavjje 22: Grafička korisnička okruženja 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, 'Greška')">
<mx:method name="dajPesme"/>
</mx:RemoteObject>
</mx:Application>
///:-

Kontejner tipa DataGrid sadrži ugnežđene oznake za svoj niz kolona. Svaki atribut ili
ugnežđeni 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
koristeći tabelaPesama kao im e prom enljive. DataGrid eksponira m n ogo više svojstava
nego što sam ja ovde pokazao; celokupni API za MXML kontrole i kontejnere m ožete naći
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 sadržaj 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 isporučuju
se zajedno s korisničkim okruženjem; u tom slučaju, u vrem e izvršavanja nem a em ito-
vanja u realnom vrem enu na zahtev.
Flash plejer sadrži ugrađene kodeke za reprodukciju i em itovanje audio i video dato-
teka raznih formata u realnom vrem enu. Flash i FIex podržavaju najkorišćenije 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 izvršavanja m ože da obavi veom a iz-
ražajne transformacije. Flexovi efekti om ogućuju da steknem o uvid u takve animacije.
Efekti su transformacije koje p om oću MXML sintakse m ožete prim eniti na kontrole i
kontejnere.
Oznaka Ettect prikazana u prethodnom MXML kodu proizvodi dva rezultata: prva
ugneždena oznaka dinam ički povećava sliku kada se miš nalazi iznad nje, a druga je di-
nam ički sm anjuje kada miš ode s nje. Ti efekti se prim enjuju na dogadaje miša dostupne
za kontrolu Slika nazvanu slikaAlbuma.
1144 Misliti na Javi

Flex im a efekte i za uobičajene anim acije kao što su prelazi, prebrisavanja i m odulira-
jući alfa kanali. Pored ugrađenih efekata, Flex podržava i Flashov API za crtanje zaista in-
ovativnih animacija. Dublje istraživanje ove tem e obuhvata grafički dizajn i anim aciju, i
prevazilazi opseg ovog odeljka.
Flex podržava kaskadne opise stilova (Cascading Style Sheets, CSS), p a znači i stan-
dardne stilove. Ako MXML datoteci p ridružite CSS datoteku, Fiex kontrole će se pridrža-
vati tih stilova. U ovom prim eru, kaskadni opis stilova stiloviPesam a.css sadrži sledeću
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.

Događaji
Korisničko okruženje je mašina stanja; ono obavlja radnje kada se m enja stanje. U Flexu
se te prom ene prijavljuju pom oću događaja. Flexova biblioteka klasa sadrži m noštvo na-
jrazličitijih kontrola koje podržavaju m noštvo događaja za sve aspekte kretanja miša i ko-
rišćenja tastature.
Prim era radi, atribut click elem enta Button predstavlja jedan od događaja dostupnih
za tu kontrolu. Vrednost dodeljena atrib u tu click m ože biti neka funkcija ili um etnut
skript. Prim era radi, u gornjoj MXML datoteci, elem ent ControlBar sadrži dugm e-
OsveziPesme za osvežavanje liste pesama. Iz oznake vidite da događaj click izaziva poziv
m etode uslugaPesam a.dajPesm e(). U tom prim eru, događaj 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: Grafička korisnička okruženja 1145

tačka s kojom klijent može da razmenjuje poruke. Usluga definisana u oznaci RemoteOb-
ject im a atribut source koji naznačuje 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 gnežđena oznaka m ethod deklariše m etodu dajPesm e(), što tu Java m etodu čini dostup-
n om ostatku Flex aplikacije.
U Flexu se svi pozivi usluga vračaju asinhrono, preko događaja koji p rouzrokuju (iza-
zivaju) te povratne funkcije. U slučaju greške, isti RemoteObject prikazuje dijalog
upozorenja.
M etodu dajPesm e() sada m ožem o pozvati iz Flasha pom oću 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 sadrži 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: Grafička korisnička okruženja 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 događaja 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, pozvaće m etodu izaberiPesm u( ) u


gornjem ActionScriptu.

Modeli podataka i povezivanje podataka


Kontrole mogu neposredno da pozivaju usluge, a povratni pozivi ActionScript događaja
daju priliku da program ski ažurirate vizuelne kontrole kada usluge vrate podatke. Iako je
skript za ažuriranje kontrola jednostavan, um e da postane obim an i zam oran, a njegove
funkcije su toliko uobičajene da FIex autom atski upravlja tim ponašanjem tako što po-
vezuje podatke.
U svom najjednostavnijem obliku, povezivanje podataka om ogućuje kontroli da
podatke referencira neposredno um esto da nekim šablonskim kodom kopira podatke u
kontrolu. Prilikom ažuriranja podataka, autom atski se ažurira i kontrola koja ih referen-
cira, bez ikakve intervencije program era. Infrastruktura Flexa tačno odgovara na događa-
je prom ene podataka i ažurira 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 vitičaste zagrade. Sve u n u tar tih vitiča-
stih zagrada Flex sm atra izrazom koji treba izračunati.
Vrednost prve kontrole, elem enta Slider (klizača), prikazuje druga kontrola - jedno
tekstualno polje. S prom enom vrednosti klizača, svojstvo tekst tekstualnog polja auto-
matski se ažurira. Na taj način, program er ne m ora da obrađuje događaje prom ena vred-
nosti elem enta Slider da bi ažurirao tekstualno polje.
Ima i sofisticiranijih kontrola, kao što su Tree (stablo) i DataGrid u aplikaciji biblio-
teke pesama. Te kontrole imaju svojstvo dataprovider koje olakšava 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 složenijeg m odela podataka, kao što je in tran et aplikacija koja
upotrebljava objekte za prenos podataka (Data Transfer Objects) ili dostavljače poruka
1148 Misliti na Javi

čiji su podaci usklađeni sa složenim šem am a, m ože p o d sta đ dalje razdvajanje izvora
podataka od kontrola. U razvoju Flex program a, to razdvajanje postižem o deklarisanjem
objekta ,,modela“, što je generički MXML kontejner podataka. Taj model ne sadrži logiku.
O n je ekvivalentan objektu za prenos podataka kakav se sreće p ri razvoju intranet pro-
gram a i odgovarajućim 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-
trošaća podataka, čim e se olakšava korišćenje obrasca model-prikaz-kontroler (Model-
View-Controller, MVC). U većim, sofisticiranijim aplikacijama, početno povećanje slože-
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 oću 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 podleže bezbednosnim ograničenjim a provere identiteta.

Izgradnja i primena
U prethodnim prim erim a mogli sm o da se provučem 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. Sledeća 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 piše
u jednom redu koji se zbog dužine 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 možete prikazati u či-
taču Weba, ali pošto u datoteci za đistribuciju koda ove knjige nem a ni MP3 datoteka niti
JPG slika, kada pokrenete aplikaciju nećete č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 ožete pokrenuti bilo koristeći menije operativnog sistema, bilo s ko-
m andne linije:

jrun -start default

Proverite da li je server uspešno p o k ren ut tako što ćete u čitaču 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 nnčin za u p o -
znavanje m ogućnosti Flexa.
Umesto da aplikaciju prevodite na kom andnoj liniji, možete 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 čitaču Weba otvaranjem adre-
se http://localhost:8700/flex/pesm e.m xm l.
Poglavlje 22: Grafička korisnička okruženja 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 način 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 odgovarajućim 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 može 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 ronađite stavku <rem ote-objects> u toj datoteci i njen odeljak <whitelist>;
videćete sledeću 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 zaključana. Ukoliko
želite da tokom razvoja program a bude đozvoljen p ristup svim klasama, uklonite znako-
ve koji od donjeg elem enta prave komentar.
Izričito preporučujem o da u kodu koji se isporučuje ne dozvolite pristupanje svim iz-
vornim datotekam a, pošto 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 > . Značenje ove i d ru -
gih stavki opisano je u dokum entim a za konfigurisanje Flexa.
Vežba 37: (3) Izgradite goreprikazan „jednostavan prim er sintakse za povezivanje poda-
taka“.
Vežba 38: (4) U datoteci koda ove knjige koja se može preuzeti sa Weba nem a ni MP3 niti
JPG datoteka navedenih u program u U slugaPesam a.java. Nađite neku sliku i pesm u
(M P3 odnosno IPG datoteku), izmenite program U slugaPesam a.java tako da sadrži 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 atične kom ponente ako ih OS ima, a pravi samo ono što OS ne-
ma. D obija se aplikacija koja korisniku liči na m atičnu, a često je i znatno brža od ekvi-
valentnog Swing program a. Pored toga, SWT-ov program ski m odel je jednostavniji od
Swingovog, što je poželjno u većini prim en a.13
Pošto SWT upošljava m atični OS koliko god je to moguće, on autom atski može da is-
koristi m ogućnosti operativnog sistema koje su Swingu nedostupne - recimo, Windows
podržava „iscrtavanje tačaka 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 ogućuje da ga probate i vi-
dite koliko se razlikuje od Swinga. Videćete da SVVT im a m nogo elem enata (vidžeta) i da
se oni prilično lako koriste. Pojedinosti m ožete da proučavate 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 više knjiga, a pišu 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 tekućeg builda Eclipsea i pronađite kom prim ovanu datoteku ćije ime počinje sa
swt i sadrži ime vaše platform e (na prim er, win32). U toj datoteci naći ćete swt.jar. Da-
toteku swt.jar najlakše ćete instalirati ukoliko je smestite u direktorijum jre/lib/ext (jer u
tom slučaju nećete m orati da m enjate svoju p u tan ju klasa). Kada dekom prim ujete biblio-
teku SWT, m ožda ćete naći još datoteka koje treba instalirati na m estim a odgovarajućim
za vašu platform u. Na prim er, W in32 distribucija obuhvata i DLL datoteke koje treba
smestiti negde u java.Iibrary.path. (O bično je ta putanja jednaka onoj koju opisuje si-
stemska prom enljiva PATH, ali stvarnu vrednost java.Iibrary.path možete saznati po-
m oću program om object/ShowProperties.java). Kada to uradite, trebalo bi da možete
transparentno da prevodite i pokrećete SWT aplikacije, kao sve druge Java programe.
D okum entacija za SWT preuzim a se zasebno.
D ruga m ogućnost je da instalirate program za uređivanje teksta Eclipse; on obuhvata
i SWT i SWT dokum entaciju koju m ožete prikazati pom oću Eclipseovog sistema pomoći.

Zdravo, SW T
Počnim o najjednostavnijom m ogućom 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: Grafička korisnička okruženja 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, videćete da kom entarska direktiva ,,Requi-
res“ završava 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 eđu operativnogsistem a i SWT-a. Klasa Shell je glavni prozor najvišeg 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 događaja, SWT ga prim orava da je
eksplicitno napiše. Na vrhu te petlje proverite da li je ljuska ugašena - to vam pruža pri-
liku da um etnete kod za čišćenje. Ali to znači da je glavna nit m a in () istovrem eno i n it ko-
risničkog okruženja. U Swingu se iza scene pravi druga nit za obradu događaja, ali u
SW T-u događaje korisničkog okruženja obrađuje glavna nit, tj. nit m etode m a in (). Pošto
se podrazum eva postojanje sam o jedne niti a ne dve, nešto je m anje verovatno da ćete ko-
risničko okruženje zatrpati nitim a.
Im ajte u vidu da zadatke ne m orate da šaljete niti korisničkog okruženja kao u Swingu.
Ne sam o da se o tom e stara SWT, nego on baca izuzetak ako vidžet pokušate da obradite
u pogrešnoj niti. M edutim , ukoliko za dugotrajne operacije treba da napravite nove niti,
događaje 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 vaša glavna nit (m a in ()) treba da pozove m etodu readAndDispatch()
za objekat Display (to znači da aplikacija može imati sam o jedan objekat tipa Display).
M etoda readA ndD ispatch() vraća true ukoliko u redu za čekanje ima više događaja koji
čekaju na obradu. 11 tom slučaju, treba je odm ah ponovo pozvati. M eđutim , ako nema
poslova koji ćekaju na izvršavanje, pozovite m etodu sle e p () objekta Display da biste neko
kratko vrem e sačekali pre nego što ponovo pozovete red za čekanje.
1152 Misliti na Javi

Kada se izvršavanje program a završi, m orate eksplicitno pozvati m eto d u d is p o s e ()


objekta Display. SWT često zahteva eksplicitno čiščenje resursa, pošto su to najčešće re-
sursi pripadnog operativnog sistema, kojih bi inače m oglo ponestati.
Sledeći program pravi više 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 , dobićete deset glavnih prozora. Zbog načina na koji je
program napisan, ako zatvorite bilo koji od tih prozora, zatvorićete i sve ostale.
I SWT koristi raspoređivače - različite od onih u Svvingu, ali iste u principu. U nared-
nom neznatno složenijem prim eru dodaćem 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: Grafička korisnička okruženja 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 vidžeti m oraju im ati roditeljski objekat opšteg tipa C o m p o site koji kon-
struktoru vidžeta 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 oću kojega možete zadati proizvoljan broj direktiva u vezi sa stilom , u zavisno-
sti od toga šta vidžet zahteva. Sve direktive u vezi sa stilom podvrgavaju se logičkoj
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. Videćete da se u SWT-u m nogo toga radi pom oću konstruktora; m noge atribute
vidžeta teško je iii nem oguće menjati sem pom oću konstruktora. U dokum entaciji vidže-
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 ogućuje buduće proširenje, a da se interfejs ne menja.

Izbegavanje redundantnosti
Pre nego što nastavimo, navešćemo š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, napišete petlju rea d A n d D isp a tc h () itd. Naravno, u
nekim posebnim slučajevima to ne m orate da radite, ali o n i su toliko retki da se isplati
sabrati sav taj kod u jedan program i izbeći ponavljanje, kao što sm o za Swing uradili u
program u net.mindvievv.util.SvvingKonzoIa.
N ateraćem 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 oću kojeg ap-
likađja m etodom createC ontents() m ora da napravi sav svoj sadržaj. U odgovarajućem
trenutku, SW TKonzola.run() poziva m etodu createC ontents(), zadaje veličinu Ijuske u
skladu sa argum entom koji je korisnik prosledio m etodi r u n (), otvara tu Ijusku, pokreće
petlju događaja i na kraju uklanja Ijusku kada program završi 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 apisaćem o varijaciju program a SvojstvaKlaseDisplay.java koji pom oću objekta tipa
SWTKonzoIa prikazuje sistemsko okruženje:

//: 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: Grafička korisnička okruženja 1155

public static void main(String [] args) {


SWTKonzola.run(new PrikaziSistemskoOkruzenje(), 800, 600);
}
} lll-~
SWTKonzola će nam om ogućiti da se usredsredim o na zanimijive aspekte aplikacije,
um esto na kod koji se ponavlja.
Vežba 39: (4) Izmenite program SvojstvaKlaseDisplay.java tako da upotrebljava klasu
SWTKonzola.
Vežba 40: (4) Izmenite program PrikaziSistemskoOkruzenje.java tako da ne upotre-
bljava klasu SWTKonzoIa.

Meniji
Prikazaćem o osnovne menije tako što će sledeći prim er ućitati sopstveni izvorni kod i
razdeliti ga na reči, 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]+"))
; // Pređi 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 ešten u neku ljusku tj. objekat tipa Shell, a nat-
klasa C om posite om ogućuje 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 rečima po-
punjava skup tipa TreeSet, pa će njihov poredak biti uređen. Početni elementi su brojevi,
koji se potom odbacuju. Pom oću toka reči daju se im ena m enijim a najvišeg nivoa u traci
menija, zatim se prave podm eniji i popunjavaju rečima dok ih ne ponestane.
Kao odgovor na izbor stavke menija, prijem nik (objekat tipa Listener) samo ispisuje
taj događaj tako da m ožete videti vrstu inform acija koje sadrži. Kada pokrenete program ,
videćete da te inform acije obuhvataju i natpis na m eniju, pa na osnovu njega možete 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 događaji


SWT im a bogat skup kontrola koje se u njem u nazivaju spravice ili vidžeti (engl. widgcts).
O snovni vidžeti su navedeni u dokum entaciji za org.eclipse.swt.widgets, a oni složeniji
u dokum entaciji od org.eclipse.sw t.custom .
Prikazaćemo nekoliko osnovnih vidžeta tako što ćem o više kom ponenata smestiti u
okna s karticam a. Videćete kako se prave objekti tipa C om posite (slično kao Swingovi
JPaneli) da bi se stavke smestile u n u ta r drugih stavki.

//; swt/0knoSKarticama.java
// Smeštanje 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: Grafička korisnička okruženja 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, "Obično 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("Klizači 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. Grafička korisnička okruženja 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 čitača");
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-
ličite vrste kartica. Tekst na svakoj kartici zadaje se m etodom setT ext() (pored toga, na
karticam a m ožete praviti dugm ad i crteže), a za svaku je zadat i njen priručni tekstualni
opis. Na kraju svake m etode poziva se setC on trol() koja kontrolu napravljenu u m etodi
smešta u prostor đijaloga te kartice.
M etoda labelTabC) prikazuje jednostavan tekstualni natpis. M etoda directorvDia-
lo g Iab ( ) sadrži dugm e koje otvara stanđardan 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č veže s trakom za prika-
zivanje napredovanja.
M etoda scribbleTab() šaljivo prikazuje grafičke m ogućnosti. Program za crtanje je
napravljen pom oću 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: Grafička korisnička okruženja 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 (površina) za crtanje, tako je u SWT-u
objekat tipa Canvas, tj. ,,platno“ (engl. canvas).
Ako ovu verziju program a uporedite sa Swing verzijom, videćete da je m etoda Crtaj-
Sinusoidu gotovo identična. U SWT-u se grafički kontekst grafKontekst dobija iz objek-
ta događaja 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
izvršavaju nad grafičkim objektom jednake, a identična je i m etoda zadajC ikluse().
Za m etodu createC ontents() p otrebno je nešto više koda nego za Swing verziju za
raspoređivanje kom ponenata i priprem u klizača i njegovog prijem nika, ali su osnovne
operacije koje se izvršavaju opet približno jednake.
1 162 Misliti na Javi

Paralelno izvršavanje u SU/T-u


Iako se AWT/Swing izvršava u jednoj niti, to je lako narušiti i proizvesti program koji se
izvršava nedeterm inistički. U suštini, prikaz ne sm ete da ispisujete pom oću više niti, ina-
če će one početi da se sudaraju na neverovatne načine.
SWT to ne dozvoljava - baciće izuzetak kada prikaz pokušate da ispišete p om oću više
niti. Tim e se sprečava da neiskusni program eri nenam erno naprave tu grešku i tako u
program unesu ,,bubicu“ koja se teško 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: Grafička korisnička okruženja 1163

getDisplay().asyncExec(new Runnable() {
public void run() {
try { redraw(); } catch(SWTException e) {}
// Izuzetak SWTException ne smeta kada je roditelj
// okončan na nižem nivou izvršavanja.
}
});
Timellni t.MILLISECONDS.sleep(pauza);
}
} catch(InterruptedException e) {
// Prihvatljiv načirl izlaska
} catch(SWTException e) {
// Prihvatljiv način 'ižlaska: roditelj
// okončan na nižem nivou izvršavanja.
}
}
}

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 može da pozove neposredno, nego m ora da je pošalje m etodi
asyncE xec() objekta tipa Display, što radi približno kao m etoda SwingUtilities.in-
vokeLater( ). Ukoliko takav poziv zam enite neposrednim pozivom m etode redraw (),
videćete da će program stati.
Kada pokrenete prethodni program , videćete 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 etićete to jasnije kada Swing verziju pokrenete na-
poredo sa SWT verzijom. Kod za dvostruko baferisanje SWT-a m ožete sam i da napišete;
prim ere za to naći ćete na Web lokaciji www.eclipse.org.
Vežba 41: (4) Izm enite swt/ObojeneKutije.java tako da počinje prskanjem tačaka
(,,zvezdica“) po platnu, kojim a se p otom boje m enjaju nasum ično.

Poređenje SWT-a i Svvinga


Iz ovako kratkog uvoda teško je steći p o tp u n u sliku, ali trebalo bi da barem počnete da
uviđate kako SWT u m nogim situacijam a om ogućuje pisanje jednostavnijeg koda nego
Swing. M eđutim , program iranje grafičkih korisničkih okruženja u SWT-u ipak je slože-
no, pa bi razlozi za korišćenje SWT-a verovatno trebalo da budu sledeći: prvo, om ogućiti
korisniku transparentniji doživljaj prilikom korišćenja vaše aplikacije (pošto ona izgleda/
ponaša se kao ostale aplikacije na platform i), i drugo, ukoliko je važna brzina reagovanja
koju SWT donosi. U protivnom , odgovarajući izbor je verovatno Swing.
Vežba 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 aći zadatak za ceo razred, pošto vodič
nesadrži rešenja.)

Sažetak
Od svih biblioteka u Javi, biblioteka grafičkih okruženja pretrpela je najveće 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 ogućavao izradu prenosivih program a, dobijeno grafičko korisničko
okruženje bilo je „podjednako osrednje na svim platformama". Bilo je ograničenih m o-
gućnosti i nezgrapno, a koristilo se teže nego m atične alatke za razvoj aplikacija dostupne
za razne platforme.
Kada su u Javu 1.1 uvedeni nov model događaja i zrna Jave, stanje je poboljšano: sada
je m oguće napraviti kom ponente grafičkog okruženja koje se lako prevlače u n u tar alatki
za vizuelno pravljenje aplikacija. O sim toga, m odel događaja i zrna jasno je pokazao kako
se vodilo računa o lakoći program iranja i održavanja koda (što nije bilo očigledno u bi-
blioteci AWT Jave 1.0). M eđutim , tek kada su se pojavile klase IFC/S\ving, posao je okon-
čan. Uz kom ponente biblioteke Svving, program iranje grafičkih okruženja prenosivih na
razne platform e postalo je relativno bezbolno.
Prava revolucija odigrala se u oblasti alatki za pravljenje aplikacija. Ako želite da se po-
boljša neka kom ercijalna alatka za pravljenje aplikacija, m orate da držite palčeve i nadate
Poglavlje 22: Grafička korisnička okruženja 1165

se da će vam autori okruženja dati ono što želite. Ipak, Java je otvoreno okruženje, što
znači da dozvoljava konkurentska okruženja za pravljenje aplikacija, i podržava takav pri-
stup. D a bi te alatke iko uzeo u obzir, one m oraju da podržavaju Javina zrna. To podra-
zum eva otvoreno igralište: ako se pojavi bolja alatka za pravljenje aplikacija, više nećete
m orati da koristite o nu koju ste dotad koristili, već možete da pređete na novu i tim e po-
većate produktivnost. Ovakva vrsta konkurentskog okruženja za vizuelne alatke ranije
nije postojala, a tržište koje se na ovaj način bude form iralo m ože 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 prikaže
osnovne tehnike za prilično lako pronalaženje sopstvenog p u ta kroz biblioteku. O no što
ste dosad videli verovatno će zadovoljiti dobar deo vaših potreba pri projektovanju kori-
sničkih okruženja. M eđutim , Swing, SWT i Flash/Flex m ogu i m nogo više od toga, jer sa-
drže p o tp u n asortim an alatki za grafička okruženja. Verovatno postoji način da
postignete gotovo sve što m ožete da zamislite.

Resursi
M režne prezentacije Bena G albraitha na adresi w w w .galbraiths.org/presentationslepo ob-
jašnjavaju i Swing i SWT.
Rešenja odabranih vcžbi data su u elektronskom dokumentu The Thinking in Java Annotated Solu-
tion Gtiide, koji se može kupiti na lokaciji www.MindView.cotn.
A: Docfaci
Ova knjiga im a više dodataka, m eđ u kojim a su i sadržaji, 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 pažnju na to da se sem inari obično drže javno, ali m ogu biti držani i privatno,
na vašoj lokaciji, sam o za vaše osoblje.

Dodaci koji se mogu preuzeti sa Interneta


Kod za ovu knjigu m ožete preuzeti na adresi w w w .M in d V iew .n et. Obuhvaćene su i Ant
build datoteke i druge datoteke za podršku neo phodne za uspešno autom atizovano
prevođenje i pakovanje program a i izvršavanje prim era iz ove knjige.
Sem toga, neki đelovi knjige prebačeni su u elektronski oblik. U njim a su obrađene teme:
• Kloniranje objekata
• Prosleđivanje i vraćanje objekata
• Analiza i projektovanje
• Delovi drugih poglavlja iz trećeg 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 ožete 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 računa o tom e da na računaru na kojem želite da pokrenete sem inar T hinking
in C m orate im ati instaliran Flash Player (koji m ožete besplatno preuzeti na lokaciji
w w w .A dobe.com ).

Seminar Thinking in Java


Moja kom panija (MindVievv, Inc.) drži 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
vežbanja pod nadzorom , kada se svakom .studentu pa/nja posvecuje pojedinaćno. Na lo-
kaciji w w w .M in dV iew .net pronaći ćete inform acije o rasporedu i m estu održavanja, sve-
dočenja bivših 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 sadrži proširenu verziju m ate-
rijala sa sem inara T h in kin g in Java. Barem ćete delim ično iskusiti kako je biti na sem inaru
a pri tom nećete putovati ni plaćati kotizaciju. Svako poglavlje ove knjige predstavljeno je
na C D -u odgovarajućim zvučnim zapisom jednog predavanja i pratećim slajdovima. Ja
sam osm islio sem inar i ja izgovaram tekst predavanja na CD -u. M aterijal je u form atu
Flash i m ože biti reprodukovan na svakoj platform i koja podržava Flash Player. CD
H a n d s-O n Java m ožete 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 viđeno očim a projektanta.
Ispituje se postupak razvoja i izgradnje sistema, prvenstveno ,,brzim“ ili ,,lakim“ m etoda-
ma, naročito m etodam a Ekstrem nog program iranja (XP). Predstavljam metodologije
uopšte, male alatke kao što je tehnika planiranja pom oću „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 slične teme.
Kurs obuhvata i jedan XP projekat koji razvijamo nedelju dana.
Ako krećete u neki projekat i želeli biste da upotrebljavate objektno orijentisane teh-
nike projektovanja, m ožem o upotrebiti vaš projekat kao p rim er i do kraja sedmice napra-
viti prvu grubu verziju rešenja.
Na lokaciji w w w .M in d V iew .n et pronaći ćete inform acije o rasporedu i m estu održa-
vanja, svedočenja bivših 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 objašnjenje 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 može preuzeti na lokađji
w w w .M in d V iew .n et. Pošto 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 eđu obrađenim tem am a biće i:
• Uvod u program iranje za poslovne prim ene
• M re/.no program iranje pom oću utičnica 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 korisničkih okruženja
• Z rna Jave u poslovnim prim enam a
• XML
• Web usluge
• A utom atsko testiranje
Tekući sadržaj knjige T hin kin g in Enterprise ja va m ožete preuzeti na lokaciji
w w w .M indV iew .net.

Thinking in Patterns (with Java)


Jedan od najvažnijih doprinosa u projektovanju objektno orijentisanih program a jesu
„projektni obrasci" čiji je razvoj hronološki opisan u knjizi Design Patterns, koju su napi-
sali G am m a, Helm , Johnson i Vlissides (Addison-Wesley, 1995). Ta knjiga opisuje 23
opšte klase problem a i njihova program ska rešenja, napisana prvenstveno na C + + -u.
Knjiga Design P atternsje postala ključni.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 stanovišta Jave. Ne bavi se sam o sa 23 klasična obrasca, nego, po potrebi, i drugim ide-
jam a i tehnikam a za rešavanje 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 obrađivan i prerađivan 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 ).
Više inform acija o toj knjizi potražite 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 držali u nekoliko proteklih godina. O n je postao preopširan, 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 drži materijala i načina predstavljanja m aterije iz knjige T h in kin g in
Patterns, pa ćete sadržaj sem inara najbolje upoznati iz te knjige, koju m ožete 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
rešenja, preko logike i evolucije rešenja do prikladnijeg dizajna. Poslednji prikazani
projekat (simulacija reciklaže smeća) evoluirao je tokom vrem ena, i njegov razvoj može
da posluži kao prototip za način na koji vaš dizajn može da nastane: kao adekvatno
rešenje određenog problem a, koje evoluira u prilagodljivo rešenje cele klase problem a.
Dodatak A: Dodaci 1169

Ovaj sem inar će vam pom oći da:


• značajno povećate prilagodljivost svojih projekata;
• dizajnirate projekat tako da bude proširiv i višekratno upotrebljiv;
• ostvarite bolju kom unikaciju o projektim a tako što ćete koristiti jezik obrazaca.
N akon svake lekcije sledi niz vežbi koje rešavate pom oću obrazaca. Bićete vođeni to-
kom pisanja program a u kojim a se konkretni obrasci p rim enjuju kao rešenja program -
skih problem a.
Na lokaciji w w w .M itidV iew .net možete pronaći inform acije o rasporedu i m estu
održavanja, svedočenja bivših polaznika i razne druge pojedinosti.

Konsultacije i revizije dizajna


Moja kom panija pruža i usluge konsaltinga, m entorskog rada, te revizije dizajna i
realizacija koie olakšavaju vodenje projekta kroz ceo njegov ciklus razvoja; sve to m ožete
dobiti i za prvi Java projekat vašeg preduzeća. Više inform acija o pojedinostim a i vre-
menskoj dostupnosti tih usluga potražite 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 ože se preuzeti
s lokacije http://java.sun.com . Čak i ako ođlučite da koristite razvojno okruženje drugih
proizvodača, uvek je dobro im ati JDK pri ruci ako naiđete na nešto što bi moglo da bude
greška prevodioca. JDK je standard, pa su greške 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 pronašao referentni priručnik o standardnim Javinim biblioteka-
m a koji nije bio zastareo ili onaj koji je sadržao dovoljnu količinu inform acija. Iako je
H TM L dokum entacija kom panije Sun bušna, im a grešaka i ponekad je toliko štura da je
p o tp u n o neupotrebljiva, barem sadrži sve klase i m etode. Ljudi često u početku im aju ot-
por prem a korišćenju Web resursa jer više vole štam panu knjigu, aii vredi uložiti malo
tru d a pa to prevazići i naviknuti se na HTM L dokum ente, barem zato što je pregled pot-
puniji nego u bilo kojoj knjizi. Tek ako ne m ožete da shvatite o čem u se radi u uputstvu
na W ebu, potražite štam pane knjige.

Programi za uređivanje teksta i alatke za pravljenje


aplikacija
U ovoj oblasti vlada zdrava konkurencija. M nogo toga je besplatno (a ono što nije, na-
jčešće se m ože besplatno isprobati), pa je najbolje da sami isprobate više alatki i nađete
onu koju vam odgovara. Evo nekih od njih:
JEdit je besplatan program za uređivanje teksta čiji je au to r Slava Pestov. Napisan je na
Javi, pa je dođatna korist to što možete videti na delu ovu lava aplikaciju za stone raču-
nare. Ovaj program za uređivanje teksta im a m nogo softverskih dodataka, od kojih su na-
jveći deo napisali članovi zajednice korisnika Jave. Preuzm ite sa adrese www .jedit.org.
N etB eans, Sunova besplatna alatka za pravljenje aplikacija koja se može preuzeti
sa w w w .netbeans.org. Projektovana za pravljenje GKO prevlačenjem kom ponenata,
za uređivanje koda, otkrivanje i otklanjanje grešaka itd.
Eclipse, projekat otvorenog izvornog koda koji podržava IBM, m eđu ostalima. Plat-
form a Eclipse je osnova koja se može proširivati, pa povrh nje možete praviti sopstvene
sam ostalne aplikađje. Deo tog projekta je biblioteka SWT opisana u poglavlju Grafička
korisnička okruženja. Preuzmite sa adrese www.Eclipse.org.
IDEA kom panije IntelliJ, omiljen komercijalni proizvod većine program era na Javi,
od kojih m nogi tvrde da je IDEA uvek korak-dva ispred Eclipsea, m ožda 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 ožete 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-
poručujem vam ovu knjigu kada pročitate M isliti na Javi i poželite da pređete na viši nivo.
The Java Class Libraries: An A nnotated Reference, Patrick C han i Rosana Lee (Addi-
son-Wesley, 1997). Nažalost, 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 ože
vam poslužiti kada zapadnete u nevolje, a izgleda da su navedena detaljnija objašnjenja
nego u većini drugih knjiga. M eđutim , Core Java 2 im a ažurnija objašnjenja m nogih
kom ponenata biblioteka.
Java N etw ork Programming, drugo izdanje, Eliotte Rusty H arold ( 0 ’Reilly, 2000).
Nisam razum eo umrežavanje u Javi (niti um režavanje uopšte, što se toga tiče) sve dok ni-
sam pronašao ovu knjigu. Sm atram i da Web lokacija autora knjige, Cafe au Lait, pruža
stim ulativan, stručan i aktuelan pogled na poboljšanja u Javi, neoptercćen obavezama
prem a bilo kom proizvođaču. Zbog redovnih ažuriranja, 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 započela razm atranje projektnih obrazaca i koja se spom inje na više
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 korišćenjem 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 izvršavaju na svim platform am a, U nix/Linux treba znati
zbog dom inacije Jave na serverima. Ericova knjiga je odličan uvod u istoriju i filozofiju
ovog operativnog sistema. Zanimljivo štivo i za one koji sam o žele da saznaju nešto o ko-
renim a računarstva.

Analiza i projektovanje
Extreme Program m ing Explained, drugo izdanje, Kent Beck uz pom oć Cynthiae Andres
(Addison-Wesley, 2005). Oduvek sam osećao da bi m orao postojati m nogo drugačiji,
m nogo bolji postupak razvijanja program a, i mislim da m u je XP prišao veom a blizu. Je-
dina kniiga koia je ostavila sličan utisak na m ene bila je Peopleware (opisana u nastavku),
koja se pretezno bavi okruženjem i opstankom u poslovnom svetu. E xtrem e Program niing
Explained govori o program iranju i posm atra m nogo toga, čak i najnovija dostignuća, 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-
juči ih i sprem ni su da ih izbace. (Prim etićete da se na knjizi ne nalazi „pečat UML“.) Od-
lučivao sam se da li ću raditi za neku kom paniju isključivo na osnovu toga da li koriste XP.
Mala knjiga, kratka poglavlja koja se lako čitaju, a teraju na razmišljanje. Počećete da za-
mišljate kako radite u takvoj atm osferi i otvoriće vam se novi vidici.
UML ukratko, M artin Fovvler (M ikro knjiga, 2004). Kada se prvi p u t sretnete s jezi-
kom UML, uplašićete se jer im a m nogo dijagram a i detalja. Prem a Fowleru, veći deo toga
je nepotreban i on će vam objasniti o n o osnovno. Za većinu 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 obeležjem postupka projektovanja. Na osnovu sopstvenog iskustva
uvideo sam da je to prem eštanje naglaska važno, jer pom aže 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 neće
svideti. Izgledalo je kao da ima sva obeležja dosadnog fakultetskog udžbenika. Ali, prijat-
no sam se iznenadio: na vrlo malo mesta sreću se objašnjenja za koja bi se m oglo pomisliti
da ni autorim a nisu jasna. Veći deo knjige ne sam o da je jasan, već je i prijatan. Najbolje
od svega je to što su objašnjenja prilično praktična. To ipak nije E xtrem e Program m ing (i
nije toliko jasna u pogledu testiranja), ali je takođe deo UML menažerije: čak i ako vam se
ne sviđa XP, većina program era je sada usvojila stav„U 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 proučavanje UML-a, i trebalo bi da je pročitate ako
posle Fowlerove knjige U M L ukratko poželite da saznate više detalja.
Pre nego što se odlučite za bilo koju m etodu, korisno je da se posavetujete s nekim ko
ne želi da vas ubedi da je koristite. Ćesto se dešava 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 privlačan razlog. Ljudi im aju jednu čudnu osobinu: ako žele da ve-
ruju kako će nešto rešiti njihove problem e, oni će to i probati. (To je eksperimentisanje,
što je dobro.) M eđutim , ako ne reše svoje problem e, m ožda će udvostručiti napore i na
sva usta objaviti kako su otkrili nešto sjajno. (To je nepriznavanje stvarnosti, što nije do-
bro.) M ožda je pouka da nećete 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 znači da nijedna m etođologija nikuda ne vodi, već da treba da budete
oprem ljeni m entalnim alatkam a koje će vam pom oći da ostanete na nivou eksperimen-
tisanja („Ovo ne radi: hajde da probam o nešto drugo.“), a da izbegavate odricanje (,,To
zapravo i nije problem . Sve ie divno, ne m oram o ništa da m eniam o"). Mislim da će vam
sledeće knjige, ako ih pročitate 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 sarađnika je P. J. Plauger), a koji izra-
žavaju njegovo dugogodišnje razm išljanje i proučavanje ove teme. Dovoljno su zabavni,
Dodatak B: Resursi 1173

a dugački sam o toliko koliko je potrebno da kažu ono što je neophodno. Takođe, radovi
nisu sam o šuplja priča; nude na stotine referenci na druge radove i istraživanja. Svi pro-
gram eri i m enadžeri trebalo bi da pročitaju 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 dlična osobina ove knjige je ta što naglas govori o onom e o čem u se ne priča: 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 može da se desi“ (ili ,,Ne može da se desi ponovo'‘) i
sm atram da to nije dobro. Ako im ate na um u da nešto uvek može da krene naopako, u
m nogo ste boljem položaju da učinite da sve radi.
Peopleware, drugo izdatije, Tom DeM arco i T im othy Lister (Dorset H ouse, 1999).
Ovu knjigu m orate da pročitate. Ne sam o da je zabavna, ona će uzdrm ati svet u kojem
prebivate i uništiti pretpostavke na kojim a počiva. Iako su autori projektanti softvera, ova
knjiga je posvećena projektim a i tim ovim a uopšte. M eđutim , istaknuta je važnost Ijudi i
njihovih potreba, a ne tehnologije i njenih potreba. A utori govore o pravljenju okruženja
u kom e će ljudi biti zadovoljni i produktivni, a ne donose pravila koja bi ljudi trebalo da
poštuju kako bi bili odgovarajući delovi mašine. Mislim da je ovaj stav najviše 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 pročitao.
Savršena je ukoliko pokušavate da radite kao konsultant ili plaćate konsultante i niste za-
dovoljni njihovim rezultatim a. Iz kratkih poglavlja ispunjenih pričam a i anegdotam a,
učite kako doći do suštine uz najm anje napora. Pročitajte 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 naučnika različitih struka u gradu Santa Fe u Meksiku, koji su raspravljali o razli-
čitim problem im a što u njihovim disciplinam a nisu rešeni (berza u ekonom iji, nastanak
života u biologiji, zašto Ijudi rade to što rade u sociologiji itd.). Suočavanjem gledišta fi-
zike, ekonom ije, hem ije, m atem atike, računarstva, sociologije i drugih nauka, razvijen je
m ultidisciplinarni p ristup tim problem im a. M eđutim , važnije je bilo to što se počelo na
drugi način razm išljati o pom enutim , veom a složenim problem im a: udaljavanje od ma-
tem atičkog determ inizm a i iluzije da se m ože napisati jednačina koja predviđa ponašanje,
i približavanje stavu da treba prvo posm a tra ti i tražiti neku pravilnost, a p otom je opona-
šati na sve m oguće načine. (Knjiga dokum entuje, na prim er, pojavljivanje genetskih al-
goritam a.) Sm atram da je ovakav način razm išljanja koristan jer ukazuje na načine
upravljanja sve složenijim 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, odličnom pratiocu Jave. Knjiga sadrži i kratak
uvod u Jython koji om ogućuje kom binovanje Jave i Pythona u istom program u (inter-
pretator jezika Jvthon pravi čist Javin bajtkod, pa vam ne treba ništa posebno da biste to
postigli). U jedinjenje ova dva jezika obećava velike m ogućnosti.
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 kući Eisys im print,
1988. M ože se naći 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 početnik. Koristio sam jezike visokog nivoa, a če-
sto i paralelni priključak računara za izvođenje raznih projekata u elektronici. Knjiga je
sastavljena od m ojih kolum ni iz M icro Cornucopiae, prvog i najboljeg časopisa za koji sam
pisao. Nažalost, M icro C je ugašen 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 ++. Više
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 tačno opisan, ali je ona
iz 1992. godine, pa je knjiga T h in k in g in C++ napisana da bi je zamenila. Više o ovoj knjizi
m ožete da saznate na lokaciji w w w .M ind V iew .n et, odakle m ožete 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). Može se preuzeti
s lokacije w w w .M ind V iew .n et. A žurirana u skladu s konačnim 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). Može se preuzeti s lokacije w w w .M in d V iew .n et.
Black Belt C++, the M aster’s Collection, Bruce Eckel, urednik (M&T Books, 1994).
Rasprodata. Zbirka radova raznih stručnjaka 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 ubuduće kontrolišem 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 eveloper’s Journal i nagradu za najbolju
knjigu po izboru čitalaca časopisa JavaWorld. Može 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. Može se
preuzeti s lokacije w w w .M in d V iew .n et.
M isliti na Javi, treće 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. Može se preuzeti s lokacije w w w .M in dV iew .n et.
Spisak termina korišćenih 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 prevođenje build izuzetak exception
i pakovanje, izgradnja izuzetak to k o m izvršavanja runtime exception
pro g ram a izvršilac executor
au to m a tsk o pakovanje autoboxing jed in ica za p revođenje 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 eširanje 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 išen itn o cooperative
deljen a brava shared lock p ro g ram ira n je multithrcading
din am ičk 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 lažni 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 eširan je, tra n sfo rm isa n je hashing najd avn ije korišćenje least-recently-used, LRU
ključa nasledivanje inheritance
identifikacija tipa u v rem e run-tim c type nastavljanje resumption
izvršavanja 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 izvršavanja thrcad oj cxccution
p rilik o m izvršavanja information, RTTI nit za o tp re m u d ogađaja 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 važenja scopc
isključiva brava exclusivc lock o b ra d a izuzetaka exception handling
ispis steka stack trace od re d ište targct
1176 Misliti na Javi

odsečak slice skladište 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 ičk 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 zaključavanje hand-over-hand locking, svojstvo property
lock coupling sv ođen je naniže downcasting
povezivač linker sv ođen je naniže koje čuva type-safe downcast
p o v ra tn i poziv callback tip
p o v ršn o ko p iran je shallow copy svo đ enje naviše 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 ključa, hashing
p rijem n ik listener heširan je
prilag o dljiv izgled pluggable look & feel učitavać klasa class loader
i p o n a ša n je ugašen 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 isključiva brava m utex
p ro g ram za u čitav anje loader u z ajam n o isključivanje 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 izvršavanja
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 išek ratn o o tk riv an je tipa m u Itiple dispa tclii ilg
red za čekanje queue višeprogram 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 učak closure
saku pljač sm eča garbage collector zapisn ik, d n ev n ik log
serv isn a nit daemon thread zaštitn 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
nasleđivanje 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, proizvođač 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 dužine, 150, 578 autor u odnosu na
izvršavanju, 1039 i generičke 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
angažovanje, Teorija Arrays operatori, 80
rastućeg, 915 asL ist(), 307, 340, 648 binarySearch( ), 624, 706
anonim na unutrašnja klasa, binarySearch( ), 624 BitSet, 716
275,721, 1053 uslužna m etoda za Bloch, Joshua, 130,805,914,
generička, 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 izvršavanje,
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 logičkim
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 generički 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 izvršavanju, 923
liste promenljivih BASIC, Microsoftov Visual optimističko
parametara BASIC, 1120 zaključavanje, 1034
( n e p o z n a t b r o j i tip
BasicArro\vButton, 1070 takmićenje za, u
argum enata), 150 Beanlnfo, namenska klasa, paralelnom
zaključivanje o tipovima 1137 izvršavanju, 1019
generičkih Beck, Kent, 1171 u paralelnom izvršavanju,
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 generičkom kodu, 512 976 450
brojanje referenci, Chain of Responsibility, classpath, sistemska
sakupljanje smeća, 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 generičke 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 oću klase
Callable, paralelno getSimpleName( ), 438 CopyOnWriteArray-
izvršavanje, 896 getSuperclass(), 438 List, 1027, 1041
case, naredba, 112 isAssignableFrom( ),454 Condition, klasa,
CASF._INSENSITIVE_ islnstance(), 453 u paralelnom
ORDER, String islnterface( ), 438 izvršavanju, 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 neobično ponavlja,
356 reference, i džokeri, 442 556
iv/'iTvisanu reć, 348 reterence, i ogranićenja, CopyOnWriteArrayList,
cepanje reči, u paralelnom 442 1003, 1027
izvršavanju, 926 RTTI pomoću objekta CopyOnWriteArraySet,
cepanje, cepanje reči, 926 tipa Class, 434 1027
1180 Misliti na Javi

CountDownLatch, za gubitak podataka izlazne prom ena ponašanja


paralelno izvršavanje, datoteke, greške i pom oću kompozicije,
983 pražnjenje bafera, 741 234
CRC32, 777 JARarhiva, 161 provera tipova u Javi, 647
crtanje na panou u Swingu, obeležja, 728 sintaksa za skupnu
1093 preslikana u mem oriju, inicijalizaciju nizova,
crtanje u Swingu, 1092 769 597
CSS (kaskadni opisi stilova), zaključavanje, 772 vezivanje, 211,214
i Adobe Flex, 1144 datoteka klase, analiza, 879 direktorijum
CyclicBarrier, za paralelno decode( ), šema kodiranja, i paketi, 168
izvršavanje, 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
čišćenje defauItR eadO bject(), 792 dispose( ), 1096
i skupljač smeća, 191 defaultW riteObject( ), 792 dodela objekata, 67
izvršavanje, 131 D eflaterOutputStream , 775 dodela vrednosti, 67
provera stanja okončanja Delayed, 990 događaj
metodom finalize(), DelayQueue, za paralelno događaji i prijemnici,
131 izvršavanje, 988 1059-1060
uz finally, 368 delegiranje, 188, 568 događaji 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 događaj, 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 događajima, 290
izuzetaka, 348 dijagram dokumentacija, 11
diiagrami nasledivanja kom entari i ugradena
D klasa, 199 dokumentacija, 57
nasleđivanje, 28 dostavljač poruka, idiom,
DatagramChannel, 773
dijalog 487, 632, 684
D atalnput, 737
okvir za, 1095 dostižni objekti i skupljanje
D atalnputStream, 733, 736,
okno s jezičcima, 1083 smeća, 710
739
za rad s datotekam a, 1099 double
D ataOutput, 737
Dijkstra, F.dsger, 978 i višenitni rad, 926
DataOutputStream, 734, 737
dim, m arker (d ili D ' literala,
datoteka
dinamička 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 ušao), 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 ključevima, 671 FileLock, 773
pom oću 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

Exchanger, klasa, filozofi, koji večeraju,
džokeri u paralelnom prim er uzajamne
i Class reference, 442. izvršavanju, 1001 blokade u paralelnom
nadtipova, 539 Executor (izvršilac), izvršavanju, 978
neograničeni, 541 paralelno izvršavanje, FilterlnputStream, 731
u generičkim 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 statičnost, 201
argum enata generičkih
(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 statični prosti tipovi, 202
faktor opterećenja, objekata finalize( ), 129, 194, 379
little endian, 763 tipa HashMap ili
entrySet( ), metoda direktno pozivanje, 131
HashSet, 701 i nasleđivanje, 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 izvršava se u slučaju 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 neograničen džokerski
OpenLaszlo, alternativa argum ent, 541
za Flex, 1138
G
niz generičkih 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
poređenju brojeva opšte nam ene, 501 samoograničeni tipovi,
s pokretnim zarezom, popunjavanje kontejnera 555
76 (podtipova klase testiranje sa @Unit, 873
marker (F) literala, 78 Collection), 500 unutrašnje klase, 508
FlowLayout, 1057 generički tipovi generisanje izuzetka, 347
Flyweight, projektni anonim ne unutrašnje 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, dužine i generičke 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 džokerski argum enti, 534 getBeanInfo( ), 1124
i interfejs Iterable, 336 džokerski 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 generičke 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 generičkih 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 neobično getPropertyType( ), 1126
ključeva (heš funkcija), ponavljaju, 556 getReadMethod( ), 1126
675 konkretizacija, 516 getSelecteđValues( ), 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
grafičko korisničko im port, rezervisana reč, 160
hash C od e()
okruženje (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
ključevima, 671
granice indikator, korišćenje skupa
na šta obratiti pažnju
i Class reference, 442 EnumSet umesto, 819
prilikom pisanja
natklasa i Class reference, InflaterlnputStream, 775
sopstvene metode,
444 inicijalizacija
678
samoograničeni generički člana klase, 181
HashMap, 664, 700, 1032,
tipovi, 555 i nasledivanje, 208
1068
u generičkim tipovima, i učitavanje klasa, 208
HashSet, realizacija skupa
514, 531 inicijalizacija instance,
Set, 322,653, 696
greška 144,277
Hashtable, 700, 714
obrada pom oću izuzetaka, inicijalizacija
hasN ex t(), m etoda klase
345 konstruktorim a
Iterator, 316
oporavak, 345 tokom nasleđivanja
heksadecimalno, 78
prijavljivanje, 385 i kompozicije, 189
Holub, Allen, 1039
prijavljivanje grešaka inicijalizacija nestatičnih
HTML u Swing
u knjizi, 14 instanci, 144
kom ponentam a, 1101
standardni izlazni tok inicijalizacija niza, 145
za greške, 350 inicijalizacija pomoću
GridBagLayout, 1058 I konstruktora, 115
GridLayout, 1058, 1119 IdentityHashM ap, 664, 700 iniđjalizatori č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
grafički korisnički ima, relacija, 21 metodama, 136
interfejs, 290, 1045 relacija kompozicije, 197 redosled inicijalizacije,
razvojna okruženja za Imagelcon, 1071 139,232
GUI, 1046 ime statičnih, 209
GZIPInputStream, 775 pravljenje jedinstvenih inicijalizovanje izvedene
GZIPOutputStream , 775 iinena paketa, 163 klase, 185
puno, 438 InputStream, 730
1184 Misliti na Javi

InputStreamReader, 735 ugnežđivanje interfejsa FileNotFoundException,


instanca unutar klasa i drugih 378
inicijalizacija instance, interfejsa, 260 fillInStackTrace( ), 359
277 za objekat, 17 finally, 367
inicijalizacija nestatičnih zajednički interfejs, 239 generički kod, 563
instanci, 144 internacionalizacija, u U/I generisanje izuzetka, 346,
klase, 16 biblioteci, 735 347
instanceof, 451 interrupt( ) gubljenje izuzetka, mana,
dinamičko ispitivanje tipa paralelno izvršavanje, 946 372
objekta m etodom višenitni rad, 912 hijerarhije klasa, 382
isln stan ce() umesto Introspector, 1124 hvatanje bilo kog izuzetka,
sa instanceof, 453 inženjering bajtkoda, 879 356
i generički 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 nasleđivanje, 374, 382
omotačka klasa, 148 rešenje pom oću i paralelnost, 924
interfejs referenci, 710 konstruktori, 377
i enum , 815 isDaemon( ), 903 menjanje mesta nastanka
i generički kod, 484 islnstance(), 453 izuzetka, 360
i nasleđivanje, 253 i generički 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 ograničenja, 374
interfejsima, 259 i foreach sintaksa, 336 ponovno generisanje
interfejs osnovne klase, i niz, 337 izuzetka, 359
218 Iterator, 332 pravljenje sopstvenih, 349
klase ugneždene unutar, Iterator, klasa, 315, 318, 332 prekidanje ili nastavljanje,
283 hasNext( ), metoda, 316 349
privatni, kao ugnežđeni next( ), metoda, 316 pretvaranje proverenih
interfejsi, 261 Iterator, projektni obrazac, u neproverene
rezervisana reč interface, 269 izuzetke, 388
242 izgled i ponašanje, 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
svođenje naviše na blok za obradu izuzetka, pronalaženje sličnih
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

tipične upotrebe javac, 57 JMenuBar, 1085,1091


izuzetaka, 390 javadoc, 58 JMenuItem, 1071,1085,
try, 368 javap, prevodilac unazad, 1090,1092
ulančani izuzeci, 388 393,479, 520 JNLP, Java Netvvork Launch
ulančavanje, 362 Javassist, 881 Protocol, 1105
vanredno stanje, 346 Javina standardna biblioteka, jo in ( ), višenitni rad, 912
zapisivanje, 352 i bezbedno višenitno JOptionPane, 1083
izvedena klasa, 214 izvršavanje, 985 Joy, Bill, 73
izvedeni tipovi, 22 JButton, 1071 JPanel, 1070,1093,1119
izvorni kod, 12 Swing, 1051 JPopupMenu, 1091
napom ena o zaštiti JCheckBox, 1071,1077 JProgressBar, 1103
autorskih prava, 12 JCheckBoxMenuItem, 1086, JRadioButton, 1071,1079
izvršavanje Java programa, 1090 JScrollPane, 1055,1082
56 JComboBox, 1080 JSlider, 1103
izvršavanje 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 svođenje naviše, 198 mašina), 434
meniji, 1085 relacija nasleđivanja, 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
uslužni 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 poništavanje, 476
Interneta na TV-u, 79, jednosm erni (događaj), 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 mašina (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 unutrašnia
prevođenje i izvršavanje 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 nasleđivanja, ugneždivanje unutar kod upravljan tabelom, 823


199 interfejsa, 283 i anonim ne unutrašnje
ekvivalentnost, unutrašnja klasa, 266 klase, 684
i instanceof/ unutrašnja klasa i prava kolekcija, 29, 306, 332
isln stan ce(), 459 pristupa, 268 klase, 302
finalne klase, 206 unutrašnja klasa popunjavanje pomoću
hijerarhije klasa i obrada i svođenje naviše, 271 Generatora, 500
izuzetaka, 382 unutrašnja klasa i Svving, spisak metoda za, 643
inicijalizacija, 440 1060 uslužne metode, 701
inicijalizacija članova, 181 unutrašnja klasa, kolekcije, parametar, 565,
inicijalizacija i učitavanje i redefinisanje, 297 589
klasa, 208 unutrašnja klasa, kom anda akcije, 1090
inicijalizacija polja, 136 i rezervisana reč kombinovane liste, 1080
inicijalizovanje izvedene super, 297 komentari i ugrađena
klase, 185 unutrašnja klasa, dokumentacija, 57
inicijalizovanje osnovne identifikatori i .class kompatibilnost
kJase, 185 datoteke, 300 vertikalna, 516
izvedena klasa, 214 unutrašnja klasa, migracijska, 516
javna klasa i jedinice u metodama komplet alatki za apstraktne
za prevođenje, 161 i oblastima važenja, prozore (Abstract
literal klase, 439, 451 273 WindowToolkit,AWT),
metode, 53 unutrašnja klasa, 1045
nasleđivanje od ugnežđivanje unutar komponenta, i zrna Jave,
apstraktnih klasa, 240 proizvoljne oblasti 1121
nasledivanje unutrašnje važenja, 274 kompozicija, 21, 180
klase, 296 upučivanje na objekat i dinamička promena
osnovna klasa, 172, 183, spoljne klase iz ponašanja, 234
214 unutrašnje klase, 270 i projektovanje, 233
podaci, 53 višestruko ugneždena, 284 kombinovanje
podobjekat, 185 klasa, učitavač, 434 kompozicije i
povezivanje, 440 klijent, programer, 20 nasleđivanja, 189
primerak, 16 klizač, 1102 u odnosu na nasleđivanje,
pristup, 175 konjunkcija 196,200, 660,714
privatna unutrašnja klasa, nad bitovima, 86 komprimovanje, biblioteka
291 logička (&&), 75 za, 775
redosled inicijalizacije, kod komunicirajući
139 izvorni, 12 sekvencijalni procesi
rezervisana reč class, 22 podela i smeštanje, 168 (CSP), 1042
statične unutrašnje klase, ponovna upotreba, 180 konferencija, za razvoj
282 standardi programiranja, softvera, 9
stil pruvljenja klasa, 174 13 konkretizacija i generićki
učitavanje, 209,440 stil program iranja, 63 tipovi, 516
ugneždena klasa (statična kod bez zaključavanja, konsalting i obuka koju
unutrašnja klasa), 282 u paralelnom pruža MindVievv, Inc.,
izvršavanju, 926 1166
Indeks 1187

konstanta statička odredba kostur upravljanja i


grupe konstantnih konstruktora, 143 unutrašnje klase, 290
vrednosti, 258 statične metode, 142 kovarijantni, 441
posredne konstante i kontejner, 29 nizovi, 535
String, 393 ispitivanje performansi, povratni tipovi, 232, 559
pri prevođenju, 200 684 tipovi argumenata, 559
uklapanje konstante, 201 klasa, 302 kritičan odeljak
konstruktor, 115 poređenje s nizom, 593 i sinhronizovani blok,
argum enti, 116 kontejneri 933
bez argum enata, 116, 124 bez zaključavanja, 1027 kvantifikator
Constructor, klasa za koji brzo otkazuju, 708 pohlepno, 413
refleksiju, 462 osnovno ponašanje, 309 posesivno, 413
i anonim ne unutrašnje za bezbeđan rad regularnog izraza, 413
klase, 275 s tipovima i generički rezervisano, 413
i finally, 377 tipovi, 303
i obrada izuzetaka, kontravarijansa i generički
376-377 kod, 539
L
i paralelno izvršavanje, 907 kontrola pristupa, 20, 178 lak
i polim orfizam, 224 konverzija objekat, 315
i preklapanje, 117 automatska, 181 trajnost, 781
inicijalizacija instance, proširujuća, 87 latentni tipovi, 572, 582
277 sužavajuća, 87 lažni objekat, 476
inicijalizacija tokoni konverzija tipova, 28 leksikografsko uređivanje,
nasleđivanja i asSubc!ass( ), 445 325
kompozicije, 189 i generički tipovi, 551 u odnosu na abecedno
konstruktor osnovne i prosti tipovi, 98 uređivanje, 623
klase, 225 operatori, 87 length
nivo pristupa automatski putem generičke klase, član niza, 147
napravljenog 553 za nizove, 595
podrazum evanog konverzija vremenskih lenja inicijalizacija, 182
konstruktora, 464 jedinica, 990 lepak, raspoređivača
podrazum evani, 124 konzola BoxLayout, 1059
ponašanje polim orfnih slanje izuzetaka na, 387 LIFO (poslednji koji uđe,
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 korisnički interfejs LinkedBlockingQueue, 971
osnovne klase sa grafički korisnički interfeis LinkedHashMap, 664, 667,
argum entim a, 186 (GUI), 290, 1045 700
redosled poziva koje brzo reaguje, I.inkedHashSet, realizacija
konstruktora uz realizacija pomoću skupa Set, 323, 653,
nasleđivanje, 224 niti, 913 696-697
1188 Misliti na Javi

LinkedList, ulančana 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 istraživanje, 661 polim orfni pozivi metoda,
649,1081 poređenje performansi, 211
poređenje performansi, 698 ponašanje polim orfnih
688 Map.Entry, 673 m etoda unutar
uređivanje i pretraživanje, MappedByteBuffer, 769 konstruktora, 230
705 m a rk (), 737 preklapanje, 117
lista m arker anotacija, 847 prim ena m etode na
grafičke liste, 1081 mašine stanja i nabrojani sekvencu, 577
padajuća lista, 1080 tipovi, 830 privatna, 232
Lister, Timothy, 1173 matcher( ), m etoda za razlikovanje preklopljenih
Listlterator, 649 pronalaženje metoda, 119
literal regularnog izraza, 414 redefinisanje privatnih,
double, 78 matches( ), m etoda klase 221
float, 78 String, 409 rekurzivna, 398
klase, 439,451 matematički operatori, 69, statična, 129, 215
long, 78 773 ugrađivanje 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 unutrašnje klase
logička za generisanje slučajnih u m etodam a
konjunkcija, 86 brojeva, 326 i oblastima važenja,
disjunkcija, 86 mehanizam za raspodelu 273
operator i nepotpuno procesorskog vremena vezivanje poziva metoda,
izračunavanje, 76 nitim a, 891 214
operatori, 75 meni zaštićene metode, 198
lokalna JDialog, JApplet, JFrame, Meyer, Jeremy, 845, 879,
unutrašnja klasa, 274 1085 1105
promenljiva, 50 JPopupM enu, 1091 Meyers, Scott, 20
long metaanotacije, 848 Microsoftov Visual BASIC,
i višenitni rad, 926 Metapodaci, 845 1120
marker (L) literala, 78 Method, 1126 migracijska kompatibilnost,
LRU, algoritam najdavnijeg klasa za refleksiju, 462 516
korišćenja, 667 M ethodDescriptor, 1126 m ikropoređenje
Ivrednost, 67 metoda perform ansi, 694
alatka za pronalaženje, miksin, 565
1061 mkdirs( ), 730
M dodavanje metoda mnemonici (prečice
main( ), 184 program u, 179 s tastatiiff), ! 090
manitest datoteka, za JAR finalna, 204,215,232 množenje, 69
datoteke, 779, 1135 generička, 496 modulo, 69
manje ili jednako (<=), 73 inicijalizacijapromenljivih m ogućnost ponovnog
manje od (<), 73 u metodam a, 136 korišćenja, 21
Indeks 1189

monitor, za paralelno operator konjunkcije (8c), unutrašnjih klasa, 296


izvršavanje, 922 79 višestruko nasleđivanje
Mono, 39 operatori, 79 u jezicima C++ i Java,
mreža objekata, 782 nadtipovi, džokerski 251
mrežni ulaz/izlaz, 753 argum enti, 539 nasleđivanje višestruke
MXML, ulazni format najdavnije korišćeno realizacije, 287
Adobe Flexa, 1138 (algoritam, LRU), 667 nastavljanje, prekidanje ili
mxmlc, prevodilac za Adobe napom ena o zaštiti nastavljanje, obrada
Flex, 1140 autorskih prava, izvorni izuzetaka, 349
kod, 12 nasum ičan izbor i nabrojani
nasleđivanje, 22,172,180, tipovi, 814
N
183,211 natklasa, 185
nabrojani tipovi, 155 dijagram, 28 ograničenje, 444
dodavanje metoda, 807 dijagrami nasleđivanja negacija, logička (!), 75
grupe konstantnih klasa, 199 neobično ponavljanje
vrednosti u jezicima i finalize( ), 226 generički tipovi, 556
C i C++, 258 i finalnost, 206 šablonski obrazac u jeziku
i interfejsi, 815 i generički kod, 484 C++, 556
i mašine stanja, 830 i nabrojani tipovi, 813 neograničen džokerski
i naredba svvitch, 809 i rezervisana reč argum ent u generičkom
i nasleđivanje, 813 synchronized, 1135 kodu, 541
i nasumičan izbor, 814 inicijalizacija i nepodržane metode, u Java
i projektni obrazac Chain nasleđivanje, 208 kontejnerim a, 646
of Responsibility, 826 kombinovanje nepotpuno izračunavanje
i uvoz statičnih članova, kompozicije i logički operatori, 76
806 i nasleđivanja, 189 nepravilan niz, 600
i višekratno otkrivanje od apstraktnih klasa, 240 neprekidnost, u paralelnom
tipa, 836 poređenjc potpunog izvršavanju, 918
metode koje se menjaju nasleđivanja i nepromenljivost, 470
u zavisnosti od kon- proširivanja, 235 nepromenljivost, kako
stante (nabrojanog preklapanje m etoda kolekciju ili m apu
tipa), 823, 840 u poređenju učiniti nepromenljivom,
rezervisana reč enum, 155, redefinisanjem, 194 706
805 projektovanje pomoću neprovereni izuzeci, 366
values( ), 805, 810 nasleđivanja, 233 pretvaranje proverenih u,
način pisanja koda, 13 proširivanje interfejsa 388
nad bitovima pom oću nasleđivanja, net.mindview.util.Swing-
disjunkcija, 86 253 Konzola, 1050
isključiva disjunkcija XOR proširivanje 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 poređenje s kontejnerom, mreža 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 dinamičke program iranje, 432
kanal, 754 agregatne pojava pseudonim a, 68
perform anse, 770 inicijalizacije, 597 postupak pravljenja, 142
nit višedimenzionalni, 599 pravljenje, 116
bezbednost, Javina vraćanje, 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 skladište niti, 940 null, 46 (idiom Prenosilac), 684
mehanizam za raspodelu Null iterator, projektni oblast važenja
procesorskog obrazac, 469 ugnežđivanje unutrašnje
vremena, 891 Null objekat, projektni klase unutar
notifyAIl( ), 957 obrazac, 469 proizvoljne oblasti
prioritet, 899 NullPointerException, 366 važenja, 274
resu m e () i uzajamna unutrašnje klase
blokada, 946
stanja, 945
o u m etodam a
i oblastima važenja,
s to p ( ) i uzajamna obavezno proveravanje 273
blokada, 946 statičkih 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 izvršavanje, 922 izvršavanja, 432
wait( ), 957 član, 21 odeljak, kritičan ođeljak
niz dodela objekata i sinhronizovani blok,
asođjativan niz, 306 kopiranjem referenci, 933
generičkih objekata, 677 67 oduzimanje, 69
inicijalizacija, 145 equals( ), 73 ograničena svojstva, 1137
kao prvorazredni objekti, finalni, 201 okno s jezičcima, 1083
595 getClass( ), 436 okruženja 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 jeđnakost, 73
OOP
objekata, 595 jednakost u odnosu osnovna obeležja, 16
poređenje elemenata, 619 na jednakost osnovni koncepti, 15
poređenje 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 generički tipovi, 497, 549
OpenLaszlo, alternativa tipove, 806 paraleino izvršavanje
za Flex, 1138 OSIzvršenje, 752 aktivni objekti, 918,1039
operacija, atomska, 926 osnova, 8,16, 78 ArrayBlockingQueue, 971
operativni sistem, osnovna klasa, 172, 183,214 BlockingQueue, 971, 988
izvršavanje programa apstraktna osnovna klasa, Brajanovo pravilo
iz Jave, 752 239 sinhronizacije, 922
operator označenog inicijalizacija, 185 cepanje reči, 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 (izvršilac), 893
operator uslovljavanja, 84 otkrivanje tipa gašenje zadataka, 942
operator uvećanja, 72 dvokratno, 836 Gecov test za izbegavanje
operatori, 66 višekratno nabrojani sinhronizacije, 926
+, za String, 393 tipovi, 836 i izuzeci, 924
binarni, 80 O utputStream, 730, 732 i kontejneri, 708
česte greške, 86 OutputStreamVVriter, 735 i Swing, 1110
konverzija u tip String označen interfejs Callable, 896
operatorom +, 67, 85 break, 108 kod bez zaključavanja, 926
logički, 75 continue, 108 konstruktori, 907
logički operatori i oznaka, 108 LinkedBlockingQueue,
nepotpuno 971
izračunavanje, 76 lokalno skladište niti, 940
nad bitovima, 79
P neprekidnost, 918
operator inđeksiranja [j, padajuća lista, 1080 nit u odnosu na zadatak,
145 paintCom ponent( ), 1093, terminologija, 911
pomeranja, 80 1098 operacije na prostim
poređenja, 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 zaštićeni 991
ternarni, 84 članovi, 197 proizvođač potrošač, 965
1192 Misliti na Javi

propušteni signali, 961 podela i smeštanje 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
takmičenje, za brave, 1019 podrazum evani paket, 161, povezano svojstvo, 1137
ulazno/izlazne operacije 170 povezivanje, klase, 440
izmedu zadataka, podupirači, rasporedivača povratni poziv, 720, 1052
realizovane pom oću BoxLayout, 1059 i unutrašnje klase, 288
cevi, 976 pohlepni kvantifikatori, 413 prazna finalna polja, 203
UncaughtException- pokazivač, nem a ga u Javi, pražnjenje 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 izvršavanju, 887
perform anse i višekratno otkrivanje prečice, s tastature, 1090
i finalnost, 207 tipa, 836 preduslovi za razumevanje
nio, 770 ponašanje polim orfnih knjige, 15
optimizacija, za paralelno m etoda unutar Preferences, API, 802
izvršavanje, 1018 konstruktora, 230 prefiksno umanjenje, 72
PhantomReference, 709 položaj, apsolutan, prilikom prefiksno uvećanje, 72
PipeđlnputStream , 731 raspoređivanja Svving prekidanje ili nastavljanje,
PipedOutputStream, kom ponenata, 1059 obrada izuzetaka, 349
731-732 polja, inicijalizacija u preklapanje
PipedReader, 735, 976 interfejsima, 259 generičkih metoda, 553
PipedWriter, 735, 976 polje za potvrdu, 1077 i konstruktori, 117
pisanje imenaa paketa pom eranje sadržaja izostanak sakrivanja
velikim slovima, 52 u Swingu, 1055 imena tokom
pisanje imena sličnih ponovna podela na proste nasleđivanja, 194
grbama kamile, 63 faktore, 159 metoda, 117
Plauger, P.J., 1172 ponovna upotreba operatora + i += za String,
početni 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 ključeva (heširanje), u odnosu na redefinisanje,
inicijalizacija statičnih, 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 poređenje 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


izvršavanja (RTTI), 237 PrintWriter, 736, 740-742 builder, 1121
ClassCastException, 445 prigodni konstruktor kostur, 290
Constructor, klasa za u Javi SE5, 746 program za uređivanje
refleksiju, 462 prioritet, paralelno teksta, pravljenje
Fielđ, klasa, 462 izvršavanje, 899 pom oću Swing klase
getConstructor( ), 1071 PriorityBlockingQueue, JTextPane, 1076
instanceof, rezervisana za paralelno izvršavanje, 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 priručni saveti, 1073 (XP), 1171
prim er obiika, 432 pristup m ultistandarđno, 16
raziika između refleksije i, k'asi, 175 objektno orijentisano,
462 kontrola, 159, 178 432
refleksija, 461 kršenje kontrole pristupa osnovni koncepti OOP-a,
svođenje naniže koje čuva pomoću refleksije, 15
tip, 445 476 programi vođeni
zloupotreba, 482 paketni i prijateljski događajima, 1052
pretraživanje pristup, 169 proizvođač potrošač, u
niza, 624 specifikatori, 20, 159, 168 paralelnom izvršavanju,
uređivanje i pretraživanje unutar direktorijuma, 965
Listi, 705 preko podrazume- projektni obrazac
preusmeravanje vanog paketa, 171 Adapter, 250, 257, 495,
standardnog unutrašnja 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
prevođenje Java programa, iluzija preklapanja Com mand, 295, 473, 822,
56 privatnih metoda, 893
prijavljivanje grešaka u 205 Decorator, 568
knjizi, 14 interfejsi, kada su Fa^ade, 452
prijem nik ugnežđeni, 261 Factory Method, 262
adapteri, 1065 metode, 232 Flyweight, 635,1043
i događaji, 1060 redefinisanje metoda, 221 Iterator, 269, 315
interfejsi, 1064 unutrašnje 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 učitavač klasa, ProcessFiles, 878 podataka (idiom
434 produžavanje Dostavljač poruka),
printff ), 400 uz očuvanje 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 rešenja, 15 744,755
Template Method, 290, proširiv program, 218 rano vezivanje, 27,214
449, 684, 772, 937, proširivanje klase tokom raspored, kontrolisanje
1025,1030 nasleđivanja, 23 pom oću rasporedivača,
Visitor, 861 proširivanje, u odnosu na 1056
projektovanje, 235 potpuno nasleđivanje, rastuće angažovanje, teorija,
dodavanje metoda 235 915
projektu, 179 proširujuća konverzija, 87 razdvajanje interfejsa
i kompozicija, 233 protected, rezervisana reč, i realizacije, 20,174,
i nasleđivanje, 233 20, 159,168, 172, 197 1060
projekat biblioteke, 159 i paketni pristup, 197 različito (!=), 73
promenljiv izgled takođe daje paketni read( ), 730
i ponašanje, 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 prevođenje, 161 i interfejs, 196, 243
ProptertyVetoException, public, rezervisana reč, 20 i razdvajanje od interfejsa,
1137 puno ime, 438 20, 174, 1060
propušteni signali, PushbacklnputStream , 733 sakrivanje, 159, 174,271
u paralelnom PushbackReader, 736 rečnik, 306
izvršavanju, 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 statični prosti sinhronizovan,
tipovi, 202
R u paralelnom
inicijalizacija polja klase, RAD (okruženje za brzo izvršavanju, 971
136 razvijanje aplikacija), redefinisanje
poređenje, 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 slučajnih 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 nasleđivanjem, 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 skladištenje 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 tačnog tipa 388 servisne niti, 901
na koji upućuje Set, 302, 306, 322,652
referenca na osnovnu
klasu, 434
s matematički odnosi, 504
poređenje performansi,
Reference, klasa paketa sabiranje, 69 696
java.lang.ref, 709 sakrivanje realizacije, 174 setActionCommand( ),
referenciranje unapred, 138 sakupljanje smeća, 129, 131 1090
refleksija, 461, 1061, 1123 dostižni objekti, 709 setBorder( ), 1075
i neobavezno i čišćenje, 191 setErr(PrintStream ), 751
proveravanje tipova, kako radi skupljač, 133 setIcon( ), 1073
387 redosled čišćenja objekata, setln(InputStream ), 751
i zrna Jave, 1121 194 setLayout( ), 1056
latentni tipovi i generički samoograničeni tipovi setMnemonic( ), 1090
kod, 576 u generičkom kodu, 555 setO ut(PrintStream ), 751
poništavanje kapsuliranja savršena funkcija za setToolTipText( ), 1073
pom oću, 476 transformisanje shuffle( ), 706
primer, 1070 ključeva, 675 signali, propušteni,
procesor anotacija, 848, Schedu!edExecutor, u paralelnom
854 za paralelno izvršavanje, izvršavanju, 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 službenika, 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 pruža 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 određivanje veličine sinhronizovane statičke i +=, 184
koji Java nema, 89 metode, 922 s p lit(), metoda, 247
skok, bezuslovni, 106 statička odredba uređivanje,
skupna inicijalizacija nizova, konstruktora, 143 CASE_INSENSITIVE
146 statički blok, 143 _ORDER, 720
slanje poruke, 17 u odnosu na dinamičku form at( ), 406
sleep (), u paralelnom proveru tipova, 647 StringBuffer, 731
izvršavanju, 897 unutrašnje 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 generički 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 h—t, 718 sufiksno umanjenje, 72
159, 168 stop( ) i uzajamna blokada, sufiksno uvečanje, 72
specijalizacija, 197 946 sukob
sp lit(), metoda klase String, Strategy, projektni obrazac, tokom heširanja, 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 unutrašnje klase, 297
SQL kod generisan putem String rezervisana reč, 185
anotacija, 850 CASE_INSENSITIVE_ supstitucija
stanje okončanja i ORDER Com parator, nasledivanje u odnosu na
finalize( ), 131 705 proširivanje, 235
standardni ulazni tok, format( ), 406 princip, 25
čitanje, 749 indexOf( ), 464 suspend( ) i uzajamna
stateChanged( ), 1095 konverzija pom oču blokada, 946
static, 243 operatora +, 67, 85 sužavajuča konverzija, 87
finalni statični prosti leksikografsko u odnosu svite, @Unit u odnosu na
tipovi, 202 na abecedno lUnit, 874
i finalnost, 201 uredivanje, 623 svodenje naniže, 200, 236
inicijalizacija, 209,436 m etoda toString( ), 181 koje čuva tip, 445
inicijalizacija statičnih metode, 398 svođenje naviše, 28, 198, 211
podataka, 140 nadovezivanje i interfejs, 245
metoda, 129, 215 operatorom + = ,85 i prepoznavanje tipa
obavezno proveravanje nepromenljivost, 392 u vreme izvršavanja,
tipova, 384 podrška za regularne 434
provera tipova, 483 izraze u klasi, 409 i unutrašnje klase, 271
Indeks 1197

System.out, omotavanje ekvivalencija tipa


svojstvo, 1121
toka u PrintW riter, 750 podataka i klase, 17
indeksirano, 1137
systemNodeForPackage(), generički tipovi i
nam enska klasa za
API Preferences, 803 kontejneri za
uređivanje svojstava,
bezbedan rad
1137
s tipovima, 303
nam enski spisak svojstava,
š izveden, 22
1137
šabloni, C++, 485, 513 latentni tipovi, 572,582
ograničena svojstva, 1137
osnovni, 22
povezana svojstva, 1137
otkrivanje tačnog tipa na
SWF, Flashov bajtkod
koji upućuje referenca
format, 1138 tabela baze podataka, na osnovnu klasu, 434
Swing, 1045 generisana SQL kodom oznaka tipa u generičkom
i paralelno izvršavanje, putem anotacija, 850
kodu, 522
1110 takmičenje, za brave, parametrizovani, 484
kom ponente, korišćenje u paralelnom
prosti, 44
HTML-a sa, 1101 izvršavanju, 1019 prosti tipovi podataka
model događaja, 1059 tastatura i kako se koriste
primeri kom ponenata, navigacija, i Swing, 1046 sa operatorim a, 89
1068 prečice, 1090 provera tipova i nizovi, 593
switch Template M ethod, projektni statička provera, 384,483
i enum , 809 obrazac, 290,449, 684, strukturni tipovi, 572
rezervisana reč, 112 772,845,850,937,1025, svođenje naniže koje čuva
synchronized, rezervisana 1030 tip, 445
reč, 921 Teorija rastućeg zaključivanje o tipovima
blok i kritičan odeijak, angažovanja, 915
generičkih
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 nasleđivanje, 1135 tehnike, 284 tion, 1130
odlučivanje 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
statičko, 922 Exception, 356 traka napredovanja, 1102
SynchronousQueue, Timer, objekat tipa, 964 transferFrom( ), 756
za paralelno TimeUnit, 898, 990 transferTo( ),756
izvršavanje, 1008 up transformisanje ključeva
System.arraycopy( ), 617 bezbednost tipova u Javi, (heširanje), 672, 674
System.err, 350, 749 86 i ključevi za heširanje, 668
System.in, 749 dinamička bezbednost savršena 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
traženje .class datoteka D ataO utput, 737 mrežni ulaz/izlaz, 753
tokom učitavanja, 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( ), zaključavanje 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 pomoču 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, unapređenje tipova, u int, u m etodam a i oblastima


731 89, 98 važenja, 273
StringReader, 735, 739 unarni ugnežđivanje unutar
String\Vriter, 735 minus (-), 71 proizvoljne oblasti
System.err, 749 operator, 80 važenja, 274
System.in, 749 operatori, 71 upućivanje na objekat
System.out, 749 plus (+), 71 spoljne klase, 270
tipične U/I konfiguracije, UncaughtExceptionHandler, zaključak, 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 pretraživanje 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- izvršavanju, 919
writeExternal( ), 785 Exception, 647 uslovno prevodenje, 168
writeObject( ), 781 unutrašnja klasa, 266 uslužne metode, klase
Writer, 730, 734-735 anonim na, 721, 1053 java.util.Collections, 701
ZipEntry, 778 generička, 508 uvećanje, operator, 72
ZipInputStream, 775 i kod upravljan tabelom, i paralelno izvršavanje,
ZipO utputStream , 775 684 920
učitavač klasa, 434 i kosturi upravljanja, 290 uzajamna blokada uprkos
učitavanje i niti, 907 izvršavanja, 1043
datoteka .class, 163 i redefinisanje, 297 uzajamna blokada,
inicijalizacija i učitavanje i rezervisana reč super, u paralelnom
klasa, 208 297 izvršavanju, 978
klase, 209, 440 i svodenje naviše, 271 uzajamno isključivanje
ugnežđena klasa (statična i Swing, 1060 (mutex), u paralelnom
unutrašnja klasa), 282 identifikatori i .class izvršavanju, 921
ugnežđivanje interfejsa, 260 datoteke, 300 uzorak, regularnog izraza,
ugradivanje poziva metoda lokalna, 274 412
direktno u kod, 204 motivacija, 285
uklapanje konstante, 201 nasleđivanje, 296
V
ulančani 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 okružujuće klase, Vector, 694, 713
jezik za modelovanje, 19, 269 večera filozola, prim er
21, 1172 statične unutrašnje klase, uzajamne blokade
pokazivanje kompozicije, 282 u paralelnom
21 izvršavanju, 978
12 0 0 Misliti na Javi

veće ili jednako (>=), 73 vraćanje rezultata Y


veće od (>), 73 i finally, 371
yPos, 797-798
vektor prom ene, 292 kovarijantni povratni
veličina, 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 zadržavanje pokazivača miša
516 u obliku niza, 597 iznad dugmeta bez
vezivanje vraćanje više objekata, pritiskanja, 1073
dinamičko, 214 487 zahtev, u OOP, 17
dinamičko, kasno, ili vrednost, sprečavanje zajednički interfejs, 239
vezivanje prilikom izmena u vreme zaključak, i unutrašnje klase,
izvršavanja, 211 izvršavanja, 200 288
kasno, 27, 214 zakljućavanje, datoteke,
poziva metoda, 214
prilikom izvršavanja, 214
w 772-773
zaključivanje, o tipu
rano, 27 w a it(), 957
generičkog 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,
višedimenzionalni nizovi, w indow C losing(), 1096
u paralelnom
599 write( ), 730
izvršavanju, 957
višekratno otkrivanje tipa nio, 756 ZipEntry, 778
i nabrojani tipovi, 836 writeDouble( ), 743
ZipInputStream, 775
pom oću m ape EnumMap, writeExternal( ), 785
ZipOutputStream, 775
842 writeObject( ), 781
zrna Jave
višeprogramski rad, 887 dodavanje m etode alatka za pravljenje
višesmerni, 1130 interfejsu Serializable,
aplikacija, 1121
događaj, i zrna Jave, 1130 791
dogadaji, 1121
višestruko nasleđivanje, Writer, 730, 734-735 EventSetDescriptor, 1126
u jezicima C ++ i Java, FeatureDescriptor, 1137
251 X getBeanInfo( ), 1124
višestruko ugnežđena klasa, getEventSetDescriptors( ),
XDoclet, 845
284 1126
XML, 799
vizuelno programiranje, XOM, XML biblioteka, 799 getM ethodDescriptors( ),
11 20 I 126
XOR uskljućiva
okruženja, 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 uređivanje 1137
Introspector, 1124 svojstava, 1137 refleksija, 1121, 1123
JAR datoteke za namenski spisak svojstva, Serializable, 1130
arhiviranje, 1135 1137 svojstva, 1121
kom ponenta, 1121 ograničena svojstva, 1137 vizuelno program iranje,
1120

You might also like