+ All Categories
Home > Documents > Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc,...

Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc,...

Date post: 17-Jan-2020
Category:
Upload: others
View: 5 times
Download: 0 times
Share this document with a friend
424
@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v jazyku Rudolf Pecinovský 2004
Transcript
Page 1: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433

Myslíme objektově

v jazyku

Rudolf Pecinovský 2004

Page 2: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 2 z 433

Stručný obsah

Stručný obsah Stručný obsah ...............................................................................................................2 Podrobný obsah ............................................................................................................4 Úvod .............................................................................................................................13

Část 1: Zapouzdření 18 1. Seznamujeme se s nástroji .................................................................................19 2. Pracujeme s třídami a objekty ............................................................................40 3. Vytváříme vlastní třídu ........................................................................................80 4. Dotváříme vlastní třídu......................................................................................176 5. Návrhové vzory..................................................................................................229

Část 2: Více tváří 239 6. Rozhraní .............................................................................................................240 7. Co takhle něco zdědit?......................................................................................291 8. Třídy mohou také dědit .....................................................................................314 9. Budete si to přát zabalit? ..................................................................................376 10. Knihovny ............................................................................................................393

Část 3: Přemýšlíme 395 11. Program začíná přemýšlet ................................................................................396 12. Ještě jednu rundu, prosím................................................................................401 13. Kontejnery nejsou jen na odpadky ..................................................................402 14. Pole .....................................................................................................................403 15. Jak nebýt výjimečný..........................................................................................404 16. Co jsme si ještě neřekli .....................................................................................405

Část 4: Přílohy 406 A Instalace vývojového prostředí ........................................................................407 B Základy práce s BlueJ .......................................................................................414 C Syntaktické diagramy........................................................................................416 D Použité projekty.................................................................................................417

Page 3: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 3 z 433

Rejstřík.......................................................................................................................420

Část 5: KONEC 424 17. Číslování řádků programu ................................................................................425 18. Odkladky ............................................................................................................432

Page 4: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 4 z 433

Podrobný obsah

Podrobný obsah Stručný obsah ...............................................................................................................2 Podrobný obsah ............................................................................................................4 Úvod .............................................................................................................................13

Použité konvence ................................................................................................................... 16

Část 1: Zapouzdření 18 1. Seznamujeme se s nástroji .................................................................................19

1.1 Trochu historie .............................................................................................................. 19 První počítače.................................................................................................................................. 19 Program musí být především spolehlivý ...................................................................................... 20

1.2 Objektově orientované programování – OOP............................................................. 21 Vývoj metodik programování ......................................................................................................... 21 Principy OOP ................................................................................................................................... 22

1.3 Překladače, interprety, platformy................................................................................. 23 Operační systém a platforma ......................................................................................................... 23 Programovací jazyky....................................................................................................................... 24

1.4 Java a její zvláštnosti .................................................................................................... 25 Objektově orientovaná ............................................................................................................... 26 Jednoduchá ................................................................................................................................ 26 Překládaná i interpretovaná ....................................................................................................... 26 Multiplatformní ............................................................................................................................ 27

1.5 Vývojové prostředí BlueJ ............................................................................................. 28 1.6 Projekty a BlueJ............................................................................................................. 29

Umístění projektů na disku ............................................................................................................ 29 Windows a substituované disky .................................................................................................... 30 Vyhledání a otevření projektu ........................................................................................................ 32

1.7 Diagram tříd ................................................................................................................... 33 Manipulace s třídami v diagramu................................................................................................... 34

1.8 Shrnutí – co jsme se naučili ......................................................................................... 38 Nově zavedené termíny .................................................................................................................. 39

2. Pracujeme s třídami a objekty ............................................................................40 2.1 Nejprve trocha teorie..................................................................................................... 40

Třídy a jejich instance..................................................................................................................... 41 Zprávy............................................................................................................................................... 41 Metody .............................................................................................................................................. 42

2.2 Analogie ......................................................................................................................... 43 2.3 Třídy a jejich instance ................................................................................................... 43

Vytváříme svou první instanci ....................................................................................................... 43 Pravidla pro tvorbu identifikátorů v jazyce Java.......................................................................... 46

Page 5: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 5 z 433

Vytváříme svou první instanci – pokračování ..............................................................................47 Posíláme instanci zprávu................................................................................................................49 Vytváříme další instance.................................................................................................................50 Rušení instancí a správa paměti ....................................................................................................50

2.4 Zprávy žádající o hodnotu.............................................................................................52 Datové typy ......................................................................................................................................52

Primitivní datové typy..................................................................................................................53 Objektové datové typy ................................................................................................................54

Vracení hodnot primitivních typů ..................................................................................................54 Vracení hodnot objektových typů ..................................................................................................55

2.5 Parametry a jejich typy ..................................................................................................58 Vyvolání konstruktoru s parametry ...............................................................................................59 Parametry objektových typů ..........................................................................................................62 Posílání zpráv s parametry .............................................................................................................63

2.6 Metody třídy....................................................................................................................64 2.7 Instance versus odkaz...................................................................................................66 2.8 Výlet do nitra instancí ....................................................................................................68

Atributy instancí ..............................................................................................................................68 Opravit ........................................................................................................................................70

Atributy třídy – statické atributy ....................................................................................................70 Opravit ........................................................................................................................................71

2.9 Přímé zadávání hodnot parametrů objektových typů .................................................73 Veřejné atributy................................................................................................................................73 Odkazy vrácené po zaslání zprávy ................................................................................................75

2.10 Shrnutí – co jsme se naučili ..........................................................................................77 Nově zavedené termíny (abecedně)...............................................................................................79

3. Vytváříme vlastní třídu ........................................................................................80 3.1 První vlastní třída ...........................................................................................................82 3.2 Zdrojový kód třídy..........................................................................................................84

Prázdná třída ....................................................................................................................................85 Implicitní konstruktor ......................................................................................................................87

3.3 Odstranění třídy .............................................................................................................88 3.4 Přejmenování třídy.........................................................................................................89 3.5 Bezparametrický konstruktor........................................................................................91 3.6 Ladění..............................................................................................................................95

Syntaktické chyby ...........................................................................................................................96 Běhové chyby ..................................................................................................................................97 Logické (sémantické) chyby...........................................................................................................99

3.7 Konstruktor s parametry .............................................................................................100 Konstruktor this.............................................................................................................................101

3.8 Testování ......................................................................................................................104 TDD – vývoj řízený testy ...............................................................................................................104 Testovací třída ...............................................................................................................................105 Přípravek ........................................................................................................................................106 Úprava obsahu přípravku .............................................................................................................107

3.9 Deklarace atributů ........................................................................................................109 Modifikátory přístupu....................................................................................................................110 Vylepšujeme Strom .......................................................................................................................110 Možné důsledky zveřejnění atributů ............................................................................................112

3.10 Syntaktické definice.....................................................................................................113 3.11 Definujeme vlastní metodu..........................................................................................115

Test vytvořených metod ...............................................................................................................117

Page 6: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 6 z 433

Nejprve testy, pak program?........................................................................................................ 120 Někdy jsou věci složitější ............................................................................................................. 123 Použití metod vracejících hodnotu.............................................................................................. 124 Definice metod vracejících hodnotu............................................................................................ 125 Parametry a návratové hodnoty objektových typů .................................................................... 126

3.12 Přetěžování .................................................................................................................. 127 3.13 Zapouzdření ................................................................................................................. 128

Rozhraní × implementace............................................................................................................. 128 Přístupové metody ........................................................................................................................ 129

Konvence pro názvy přístupových metod ................................................................................ 131 Kontrakt.......................................................................................................................................... 132

3.14 Kvalifikace a klíčové slovo this.................................................................................. 132 Kvalifikace metod.......................................................................................................................... 132 Kvalifikace atributů ....................................................................................................................... 134

3.15 Atributy a metody třídy (statické atributy a metody)................................................ 136 Atributy třídy .................................................................................................................................. 136 Metody třídy ................................................................................................................................... 137

3.16 Lokální proměnné........................................................................................................ 139 3.17 Konstanty a literály ..................................................................................................... 143

Konstanty objektových typů ........................................................................................................ 145 Správná podoba literálů ............................................................................................................... 146

boolean..................................................................................................................................... 146 int .............................................................................................................................................. 146 double....................................................................................................................................... 146 String ........................................................................................................................................ 147 null ............................................................................................................................................ 148

3.18 Komentáře a dokumentace......................................................................................... 148 Tři druhy komentářů ..................................................................................................................... 149 Uspořádání jednotlivých prvků v těle třídy................................................................................. 157 BlueJ a komentářová nápověda .................................................................................................. 158 Automaticky generovaná dokumentace ..................................................................................... 160 Dokumentace celého projektu ..................................................................................................... 161 Pomocné značky pro tvorbu dokumentace................................................................................ 164

@author.................................................................................................................................... 164 @version .................................................................................................................................. 164 @param.................................................................................................................................... 164 @returns................................................................................................................................... 164

3.19 Závěrečný příklad – UFO ............................................................................................ 165 Třída Dispečer................................................................................................................................ 166

Jednodušší varianta ................................................................................................................. 166 Varianta ovládaná z klávesnice................................................................................................ 166

Třída UFO ....................................................................................................................................... 167 Třída UFOTest................................................................................................................................ 168

3.20 Vytvoření samostatné aplikace .................................................................................. 168 Třída spouštějící aplikaci.............................................................................................................. 169 Vytvoření souboru JAR s aplikací ............................................................................................... 169

3.21 Shrnutí – co jsme se v kapitole naučili...................................................................... 171 4. Dotváříme vlastní třídu......................................................................................176

4.1 Jednoduché vstupy a výstupy ................................................................................... 176 Doplnění projektu o třídu odjinud................................................................................................ 177 Textové řetězce.............................................................................................................................. 177

Rozdíl mezi prázdným řetězcem a null .................................................................................... 179 Čísla ................................................................................................................................................ 180

Page 7: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 7 z 433

4.2 Knihovny statických metod.........................................................................................181 4.3 Podrobnosti o operátorech .........................................................................................181

Binární aritmetické operátory + – * / %..............................................................................182 Sčítání, odčítání, násobení .......................................................................................................182 Slučování řetězců + ..................................................................................................................183 Dělení / .....................................................................................................................................183 Zbytek po dělení (dělení modulo) %.........................................................................................184

Unární operátory + –..................................................................................................................185 Kulaté závorky ( ) .........................................................................................................................185 Přiřazovací operátor =...................................................................................................................186 Sdružené přiřazovací operátory +=, –=, *=, /=, %= .....................................................................187 Operátor přetypován (typ) ............................................................................................................188

Pseudopřetypování na String ...................................................................................................189 4.4 Počítáme instance........................................................................................................190 4.5 Inkrementační a dekrementační operátory ................................................................191

Odbočka o obecných zásadách programování ..........................................................................194 Jiný způsob inicializace rodného čísla .......................................................................................196

4.6 Standardní výstup........................................................................................................196 4.7 Metoda toString()..........................................................................................................198 4.8 Prázdná standardní třída .............................................................................................199 4.9 V útrobách testovací třídy ...........................................................................................202

Přípravek ........................................................................................................................................204 Automaticky generované testy ....................................................................................................206 Vlastní testy....................................................................................................................................207 Úklid ................................................................................................................................................208 Metody assertEquals a assertTrue ..............................................................................................208 Test testů ........................................................................................................................................209

4.10 Debugger a práce s ním...............................................................................................211 Krokování programu .....................................................................................................................212 Okno debuggeru ............................................................................................................................215

Vlákna.......................................................................................................................................216 Pořadí volání.............................................................................................................................216 Atributy třídy..............................................................................................................................217 Atributy instancí ........................................................................................................................217 Lokální proměnné .....................................................................................................................217

Atributy a proměnné objektových typů.......................................................................................218 Už nezastavuj – ruším zarážky .....................................................................................................220 Předčasný konec programu .........................................................................................................220 Pozastavení běžícího programu...................................................................................................221 Krokování konstruktorů ................................................................................................................221

4.11 Hodnotové a referenční typy.......................................................................................222 Hodnotové typy..............................................................................................................................222 Referenční datové typy .................................................................................................................223 Program demonstrující rozdíl.......................................................................................................223

4.12 Projekt Zlomky .............................................................................................................225 4.13 Shrnutí – co jsme se naučili ........................................................................................226

Nové termíny ..................................................................................................................................228 5. Návrhové vzory..................................................................................................229

Opravit ......................................................................................................................................229 5.2 Přepravka (Messenger)................................................................................................230 5.3 Jedináček (Singleton) ..................................................................................................233 5.4 Výčtové typy .................................................................................................................237

Page 8: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 8 z 433

5.5 Shrnutí – co jsme se naučili ....................................................................................... 237 Nové termíny.................................................................................................................................. 238

Část 2: Více tváří 239 6. Rozhraní .............................................................................................................240

6.1 Kreslíme jinak .............................................................................................................. 241 6.2 Rozhraní jako zvláštní druh třídy ............................................................................... 242 6.3 Instance rozhraní......................................................................................................... 244 6.4 Nový projekt................................................................................................................. 244

Práce s novým plátnem ................................................................................................................ 248 Událostmi řízené programování................................................................................................... 250

6.5 Implementace rozhraní ............................................................................................... 250 Implementace rozhraní v diagramu tříd ...................................................................................... 251 Implementace rozhraní ve zdrojovém kódu ............................................................................... 252

6.6 Úprava zdrojového kódu třídy Strom ........................................................................ 252 Třída musí jít přeložit .................................................................................................................... 253 Testování........................................................................................................................................ 256 Závěrečné úpravy.......................................................................................................................... 261

Uložení odkazu na Plátno do atributu třídy .............................................................................. 261 Odstranění statického atributu krok.......................................................................................... 262 Úpravy posunových metod....................................................................................................... 262 Zefektivnění přesunu................................................................................................................ 262

Efektivita vykreslování.................................................................................................................. 263 6.7 Implementace několika rozhraní ................................................................................ 264

Odvolání implementace rozhraní................................................................................................. 265 6.8 Návrhový vzor Služebník (Servant) ........................................................................... 266

Proč rozhraní ................................................................................................................................. 267 Implementace................................................................................................................................. 268 Aplikace na náš projekt ................................................................................................................ 269 Závěrečný test ............................................................................................................................... 270

6.9 Refaktorování............................................................................................................... 272 Ukázka ............................................................................................................................................ 273

1. krok: Vytvoření testu............................................................................................................. 274 2. krok: Definice nových atributů .............................................................................................. 275 3. krok: Kopírování těla konstruktoru do těla metody............................................................... 276 4. krok: Dočasné „odkonstatnění“ některých atributů............................................................... 277 5. krok: Definice potřebných lokálních proměnných................................................................. 277 6. krok: Odstranění tvorby nových instancí koruny a kmene ................................................... 277 7. krok: Vrácení koruny a kmene mezi konstanty .................................................................... 278 8. krok: Vyvolání metody setRozměr(int,int) v konstruktoru..................................................... 278 9. krok: Odstranění zdvojeného kódu z konstruktoru............................................................... 278 10. krok: Doplnění metody setRozměr(Rozměr) ...................................................................... 279 11. krok: Doplnění metody setOblast(Oblast) .......................................................................... 279

6.10 Projekt Výtah................................................................................................................ 280 Analýza problému.......................................................................................................................... 281

Okolí ......................................................................................................................................... 281 Konstruktory ............................................................................................................................. 281 Potřebné metody ...................................................................................................................... 282

Implementace................................................................................................................................. 283 Implementovaná rozhraní......................................................................................................... 284 Atributy ..................................................................................................................................... 284 Postup při návrhu metod .......................................................................................................... 285

Page 9: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 9 z 433

Metoda doPatra(int) ..................................................................................................................285 Metoda přijeďK(IPosuvný) ........................................................................................................285 Metoda nástup(IPosuvný).........................................................................................................286 Metody výstupVpravo() a výstupVlevo()...................................................................................286 Test převozu pasažéra .............................................................................................................286 Metody odvezVpravo(IPosuvný,int) a odvezVlevo(IPosuvný,int) .............................................287

6.11 Shrnutí – co jsme se naučili ........................................................................................288 Nové termíny ..................................................................................................................................290

7. Co takhle něco zdědit? .....................................................................................291 7.1 Co to je, když rozhraní dědí? ......................................................................................292 7.2 Jak to zařídit .................................................................................................................293

Duplicitně deklarovaná implementace ........................................................................................294 7.3 Společný potomek několika rozhraní.........................................................................294 7.4 Návrhový vzor Stav (State)..........................................................................................298

Projekt Šipky ..................................................................................................................................298 Shrnutí ............................................................................................................................................303 Projekt Robot .................................................................................................................................304

7.5 Návrhový vzor Zástupce (Proxy) ................................................................................304 7.6 Projekt Kabina ..............................................................................................................306

Předpřipravené třídy......................................................................................................................307 Multipřesouvač..........................................................................................................................307 IMultiposuvný............................................................................................................................307 IZastávka ..................................................................................................................................308 Linka .........................................................................................................................................308

Úloha – Kabina...............................................................................................................................309 Projekt Šipky ................................................................................... Chyba! Záložka není definována. Shrnutí ............................................................................................. Chyba! Záložka není definována. Projekt Robot .................................................................................. Chyba! Záložka není definována.

7.7 Shrnutí – co jsme se naučili ........................................................................................313 Nově zavedené termíny.................................................................................................................313

8. Třídy mohou také dědit .....................................................................................314 8.1 Podtřídy a nadtřídy ......................................................................................................315

Specializace ...................................................................................................................................315 Zobecnění.......................................................................................................................................315 Realizace v OOP ............................................................................................................................316 Univerzální (pra)rodič....................................................................................................................317

8.2 Experimenty s dědičností............................................................................................318 Univerzální rodič Object................................................................................................................319 Atributy a bezparametrické konstruktory tříd v projektu ..........................................................320 Hierarchie dědičnosti ....................................................................................................................322 Podobjekt rodičovské třídy ..........................................................................................................324 Explicitní volání konstruktoru předka .........................................................................................327 Chráněné atributy – modifikátor přístupu protected .................................................................329 Dědičnost a metody tříd................................................................................................................330 Metody instancí, jejich dědění a překrývaní ...............................................................................331

Nové metody.............................................................................................................................332 Nepřekryté zděděné metody.....................................................................................................332 Překryté zděděné metody.........................................................................................................332

Test chování překrývajících a překrytých metod .......................................................................334 Porovnání .................................................................................................................................337 Podobjekt ..................................................................................................................................337 Soukromá metoda ....................................................................................................................337 Veřejná metoda ........................................................................................................................338

Page 10: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 10 z 433

Instance vnučka........................................................................................................................ 338 Vyvolání překryté verze metody .................................................................................................. 338

8.3 Vytváříme dceřinou třídu ............................................................................................ 340 Jednoduchá dceřiná třída............................................................................................................. 341 Konstruktory potomka.................................................................................................................. 343 Složitější dceřiná třída .................................................................................................................. 344

Příprava prázdné třídy a testovací třídy ................................................................................... 344 Definice konstruktorů................................................................................................................ 345 Metoda kresli(java.awt.Graphics2D) ........................................................................................ 346 Metoda setPozice(int,int) .......................................................................................................... 346

Jak přesvědčit objekt, aby se pokaždé choval jinak ................................................................. 349 Samostatná úloha: Terč................................................................................................................ 351

8.4 Vytváříme rodičovskou třídu ...................................................................................... 354 Společný rodič Posuvný............................................................................................................... 354

Příprava .................................................................................................................................... 354 Konstantní atributy třídy ........................................................................................................... 356 Proměnné atributy třídy ............................................................................................................ 356 Konstantní atributy instancí ...................................................................................................... 356 Proměnné atributy instancí....................................................................................................... 357 Konstruktory ............................................................................................................................. 357 Metody instancí ........................................................................................................................ 358

Doladění dceřiných tříd ................................................................................................................ 359 Elipsa, Obdélník, Trojúhelník ................................................................................................... 359 Čára.......................................................................................................................................... 360 Text........................................................................................................................................... 360 Strom ........................................................................................................................................ 361

Společný rodič Hýbací.................................................................................................................. 362 8.5 Abstraktní metody a třídy ........................................................................................... 363

Neimplementovaná metoda implementovaného rozhraní ........................................................ 364 Nově deklarovaná abstraktní metoda.......................................................................................... 365 Abstraktní třída bez abstraktních metod..................................................................................... 366

8.6 Návrhový vzor Stav podruhé...................................................................................... 367 Projekt Šipka.................................................................................................................................. 367

8.7 Co je na dědičnosti špatné ......................................................................................... 369 8.8 Kdy (ne)použít dědičnost............................................................................................ 370

Co jsme dělali špatně.................................................................................................................... 371 Kdy dát přednost skládání a kdy dědičnosti .............................................................................. 372

8.9 Shrnutí – co jsme se naučili ....................................................................................... 372 Nově zavedené termíny ................................................................................................................ 374

9. Budete si to přát zabalit? ..................................................................................376 9.1 Velké programy a jejich problémy ............................................................................. 376 9.2 Balíčky.......................................................................................................................... 377

Podbalíčky...................................................................................................................................... 378 Uspořádání podbalíčků s programy k dosavadní části knihy .................................................. 379

9.3 Balíčky a BlueJ ............................................................................................................ 380 Příprava stromu balíčků pro BlueJ ve správci souborů............................................................ 380

9.4 Hrajeme si s balíčky .................................................................................................... 381 Rodičovský balíček v diagramu tříd ............................................................................................ 383 Převedení obyčejné složky na balíček v BlueJ .......................................................................... 384 Vytvoření nového balíčku v BlueJ ............................................................................................... 384 Putování stromem balíčků............................................................................................................ 384 Automatická redefinice příkazu package.................................................................................... 385

9.5 Jmenné prostory a příkaz import............................................................................... 386

Page 11: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 11 z 433

Import více tříd...............................................................................................................................386 Podbalíčky......................................................................................................................................387 Balíček java.lang............................................................................................................................388 Nevýhody koncepce balíčků v BlueJ...........................................................................................388 Přemosťovací třída........................................................................................................................388

9.6 Přístupová práva v rámci balíčku ...............................................................................389 9.7 Tvorba vlastních aplikací.............................................................................................390 9.8 Knihovny.......................................................................................................................391 9.9 Shrnutí – co jsme se naučili ........................................................................................391

Nové termíny ..................................................................................................................................392 10. Knihovny ............................................................................................................393

10.1 Pracujeme s náhodou..................................................................................................393 10.2 Návrhový vzor Adaptér................................................................................................393 10.3 Pracujeme s obrázky ...................................................................................................394 10.4 Přetypovávání na rodiče a na potomka......................................................................394 10.5 Shrnutí – co jsme se naučili ........................................................................................394

Nově zavedené termíny.................................................................................................................394

Část 3: Přemýšlíme 395 11. Program začíná přemýšlet ................................................................................396

11.1 Jednoduché rozhodování............................................................................................396 Třídy jako objekty ..........................................................................................................................398

11.2 Výběr ze dvou možností ..............................................................................................399 11.3 Když – jinak ..................................................................................................................399 11.4 Násobný výběr, ............................................................................................................399 11.5 Přepínač ........................................................................................................................399 11.6 Shrnutí – co jsme se naučili ........................................................................................399

Nově zavedené termíny.................................................................................................................400 12. Ještě jednu rundu, prosím................................................................................401

12.1 Podkapitola...................................................................................................................401 12.2 Shrnutí – co jsme se naučili ........................................................................................401

Nové termíny ..................................................................................................................................401 13. Kontejnery nejsou jen na odpadky ..................................................................402

13.1 Podkapitola...................................................................................................................402 13.2 Shrnutí – co jsme se naučili ........................................................................................402

Nově zavedené termíny.................................................................................................................402 14. Pole.....................................................................................................................403

14.1 Podkapitola...................................................................................................................403 14.2 Shrnutí – co jsme se naučili ........................................................................................403

Nově zavedené termíny.................................................................................................................403 15. Jak nebýt výjimečný..........................................................................................404

15.1 Podkapitola...................................................................................................................404 15.2 Shrnutí – co jsme se naučili ........................................................................................404

Nově zavedené termíny.................................................................................................................404 16. Co jsme si ještě neřekli.....................................................................................405

16.1 Podkapitola...................................................................................................................405 16.2 Shrnutí – co jsme se naučili ........................................................................................405

Nově zavedené termíny.................................................................................................................405

Page 12: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 12 z 433

Část 4: Přílohy 406 A Instalace vývojového prostředí ........................................................................407

A.1 Instalace Java 2 SDK................................................................................................... 407 A.2 Instalace prostředí BlueJ............................................................................................ 409

Instalace prostředí......................................................................................................................... 409 Instalace rozšíření ......................................................................................................................... 409 První spuštění................................................................................................................................ 410 Konfigurace BlueJ......................................................................................................................... 411

B Základy práce s BlueJ .......................................................................................414 C Syntaktické diagramy........................................................................................416 D Použité projekty.................................................................................................417

02_Objekty ............................................................................................................................... 417 03_Třídy_A ............................................................................................................................... 417 03_Třídy_Z ............................................................................................................................... 417 03_UFO .................................................................................................................................... 417 04_Zlomky ................................................................................................................................ 418 05_Vzory .................................................................................................................................. 418 06_Rozhraní_A......................................................................................................................... 418 06_Rozhraní_Z......................................................................................................................... 418 07_Dědění_rozhraní_Z ............................................................................................................ 418 08_Dědičnost_tříd_pokusy....................................................................................................... 418 08_Dědičnost_tříd_A................................................................................................................ 418 08_Dědičnost_tříd_B................................................................................................................ 419 08_Dědičnost_tříd_C................................................................................................................ 419 08_Dědičnost_tříd_D................................................................................................................ 419 08_Dědičnost_tříd_E................................................................................................................ 419

Rejstřík .......................................................................................................................420

Část 5: KONEC 424 17. Číslování řádků programu ................................................................................425 18. Odkladky ............................................................................................................432

18.1 Podkapitola .................................................................................................................. 432

Page 13: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 13 z 433

Úvod

Úvod Otevíráte knížku, která vás chce naučit programovat moderním, objektově orien-tovaným stylem. Rozhodnete-li se s její pomocí vstoupit do světa programování, naučíte se vytvářet programy tak, abyste se za ně nemuseli ani po několika letech stydět.

Už podle její tloušťky jste nejspíše odhadli, že není určena těm, kteří hledají dvousetstránkovou rychloučebnici, s jejíž pomocí se naučí programovat za ví-kend. Jestli jste tuto knihu otevřeli, tak asi patříte k těm, kteří vědí, že taková učebnice neexistuje. Dvousetstránková knížka bude možná levná, ale může věci pouze naznačovat, takže se její čtenář dostane ke skutečnému poznání až po dlou-hém usilovném samostudiu.

Tato knížka je určena pro ty, kteří to se svoji touhou naučit se programovat myslí vážně a chtějí se naučit programovat dobře. Nejsem přítelem stručných ná-znaků. Naopak, budu se v ní snažit vám předvést všechny klíčové dovednosti krok za krokem a ukázat vám úskalí, která vás mohou očekávat při tvorbě vašich vlastních programů.

Musím vás také upozornit na to, že tato knížka se od běžných učebnic, s ni-miž se můžete v současné době v knihkupectví setkat, poněkud liší. Současné učebnice programování jsou totiž většinou především učebnicemi nějakého pro-gramovacího jazyka. Jejich autoři se proto ve svém výkladu soustředí hlavně na výklad vlastností popisovaného jazyka a snaží se jich čtenářům vysvětlit co nejví-ce. Předpokládají přitom, že vedlejším produktem jejich výkladu bude to, že se čtenář naučí současně programovat.

Mé letité zkušenosti s programátory, které přeučuji z klasického programo-vání na programování objektově orientované, však ukazují, že tohoto výsledku bývá dosaženo jen zřídka. Většina programátorů, kteří přicházejí do mých kurzů, zná poměrně dobře konstrukce svého programovacího jazyka, bohužel pouze ně-kteří z nich v něm umí dobře programovat.

Dopředu proto říkám: toto není učebnice programovacího jazyka, toto je učebnice programování. Mým cílem není naučit vás všem finesám a zákoutím použitého programovacího jazyka, ale naučit vás především efektivně navrhovat a vytvářet spolehlivé a snadno udržovatelné programy. Jinými slovy: chci vás na-učit dovednostem, které budete moci použít, i když budete programovat v jiném programovacím jazyce (a k tomu určitě dojde – já sám jsem jich byl nucen vystří-dat za svoji praxi 24). Jazyky přicházejí a odcházejí. Základní programátorské

Page 14: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

14 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 14 z 433

techniky a způsob myšlení však žijí daleko déle než jazyky, s kterými byly zave-deny.

Výuce programovacího jazyka se však nevyhneme. Budete-li si chtít vyzkou-šet to, co jste se naučili, nezbude vám, než program v nějakém programovacím ja-zyce vytvořit. Pro demonstraci látky vysvětlované v této učebnici budu používat programovací jazyk Java. Zvolil jsem jej z několika důvodů:

v současné době je to nejrozšířenější programovací jazyk,

je to moderní programovací jazyk, na němž lze demonstrovat použití všech důležitých postupů,

programovací jazyk i vývojové nástroje je možné získat zdarma,

vytvořené programy nejsou omezeny ne jediný operační systém, ale chodí prakticky všude,

je k němu k dispozici vývojový nástroj specializovaný pro výuku, který je dokonce lokalizovaný do češtiny.

Jak jsem již řekl, v této učebnici se chci soustředit spíše na to, jak programovat, a programovací jazyk používám pouze jako prostředek k tomu, abychom si mohli vysvětlené věci hned také vyzkoušet. Zkoušet ale budeme hodně, takže se v prů-běhu výuky naučíte nejenom programovat, ale zároveň získáte potřebnou praxi při řešení nejrůznějších úloh v programovacím jazyku Java.

Nebudeme spolu řešit pouze jednoduché úlohy, jejichž hlavním účelem je demonstrovat vysvětlovanou vlastnost jazyka (i když se jim nebudu vyhýbat), ale budu se vám naopak snažit předkládat i úlohy složitější, a to i za cenu toho, že část úlohy, která bude používat doposud nevysvětlené konstrukce, za vás budu muset předem vyřešit sám. Takovéto úlohy daleko lépe odpovídají těm, s nimiž se budete v praxi setkávat. Typickou úlohou programátora totiž není navrhnout kompletní řešení nějakého jednoduchého problému, ale naopak doplnit stávající, většinou velmi složitý a někým jiným napsaný program, o nějakou novou funkci.

Při práci na takovýchto příkladech si vyzkoušíte další potřebnou dovednost, kterou je schopnost orientovat se v programu, který je mnohem složitější, než byste sami dokázali v daném okamžiku naprogramovat.

Tato kniha se od ostatních učebnic programování liší ještě v jedné věci: větši-na ostatních učebnice sice na začátku vysvětlí, co je to objektové programování, ale pak na něj na chvíli zapomenou a začnou výkladem klasických programova-cích konstrukcí. My to uděláme právě obráceně. Jestli se vám má dostat objektově orientované myšlení pod kůži, musíme s jeho výkladem začít hned a nezatěžovat vás napřed klasickými konstrukcemi, které by vaše myšlení směřovaly trochu ji-nam, takže byste se museli po chvíli zase přeorientovávat. Na klasické konstrukce

Page 15: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Úvod 15

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 15 z 433

samozřejmě nezapomeneme, ale dojde na ně řada až v době, kdy již budete mít za sebou několik objektově orientovaných programů, které budete moci pomocí těchto konstrukcí dále vylepšovat.

Kniha je rozdělena do čtyř částí. V první části se seznámíte s třídami a objekty a naučíte se pracovat s vývojovým prostředím. Druhá část před vámi odkryje klí-čová zákoutí objektově orientovaného programování a naučí vás přemýšlet při tvorbě programů objektově. Třetí část pak doplní vaše znalosti o klasické progra-mové konstrukce a ukáže vám, jak se řeší složitější úlohy z praktického života.

Celou knihou se potáhne jedna úloha, která musí být dostatečně jednoduchá, abyste na ní vykládanou látku co nejlépe pochopili. Touto úlohou bude práce s grafickými tvary zobrazovanými na simulovaném plátně. Při práci s grafickými tvary si můžeme postupně vysvětlit i ty nejsložitější programátorské obraty způ-sobem, který je dostatečně průzračný a pochopitelný.

Abyste nepodlehli dojmu, že programování spočívá pouze v kreslení grafic-kých tvarů, budou pro vás paralelně k této úloze připraveny další, většinou složi-tější (a doufám i zajímavější) úlohy, na kterých si budete moci vyzkoušet, jak jste vykládanou látku pochopili.

Úlohy v první části knihy budou, pravda, ještě triviální, protože toho ještě nebudete moc umět. Druhá část už přinese zajímavější úlohy s nejrůznějšími ani-macemi. Ve třetí části se pak rozmáchneme a začneme řešit úlohy, z nichž některé se mohou stát základem vašich budoucích programů.

Žádná z úloh zadaných v této knize není pouze zadána, všechny úlohy jsou vyřešeny, abyste si mohli porovnat své řešení se vzorovým a abyste se v případě, kdy se dostanete do těžkostí a nebudete si vědět rady, měli kam obrátit pro inspi-raci. Budete-li se však chtít opravdu naučit programovat, doporučuji vám vyřešit všechny tyto doplňkové úlohy vlastní hlavou.

Na závěr tohoto úvodu dovolte ještě jednu omluvu. Vím, že kniha je výrazně tlustší, než začátečnické učebnice obvykle bývají. Není to proto, že bych toho v ní chtěl vysvětlit tak moc, je to spíš proto, že se snažím všechna krizová místa spolu s vámi projít krok za krokem. Nechtěl jsem jen naznačovat, na co si máte dát po-zor, ale u většiny častých chyb jsem také uvedl příklad, v němž se chyba nebo ne-příjemná situace vyskytuje, a zároveň ukázal řešení nebo potřebnou reakci. Protože je ale takových míst hodně, kniha nám trochu narostla. Doufám, že i vy budete tyto podrobné vysvětlivky považovat za užitečné.

Page 16: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

16 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 16 z 433

Použité konvence K tomu, abyste se v textu lépe vyznali a také abyste si vykládanou látku lépe za-pamatovali, používám několik prostředků pro odlišení a zvýraznění textu.

Objekty Názvy objektů v programu a další texty, které chci zvýraznit, vysazuji tučně.

Názvy Názvy firem a jejích produkt vysazuji kurzivou. Kurzivou vysazuji ta-ké názvy kapitol, podkapitol a oddílů, na které se v textu odkazuji.

Citace Texty, které si můžete přečíst na displeji, např. názvy polí v dialogo-vých oknech či názvy příkazů v nabídkách, vysazuji tučným bezpatkovým písmem.

Adresy Názvy souborů a internetové adresy vysazuji obyčejným bezpatkovým písmem.

Program Texty programů a jejich částí vysazuji neproporcionálním písmem..

Kromě částí textu, které považuji za důležité zvýraznit nebo alespoň odlišit od okolního textu, najdete v textu ještě řadu doplňujících poznámek a vysvětlivek. Všechny budou v jednotném rámečku, který bude označen ikonou charakterizují-cí druh informace, kterou vám chce poznámka či vysvětlivka předat.

☯ Symbol jin-jang bude uvozovat poznámky, s nimiž se setkáte na počát-ku každé kapitoly a ve kterých si povíme, co se v dané kapitole naučí-me.

Otevřená schránka s dopisy označuje poznámku oznamující název předpřipraveného projektu, s nímž budeme v dalším textu pracovat a případně některé další podrobnosti. Všechny tyto projekty jsou stručně popsané v příloze Použité projekty na straně 417 a naleznete je na adrese http://vyuka.pecinovsky.cz, odkud si je můžete stáhnout. Určitě byste je měli mít stažené a připravené před tím, než začnete knížku doopravdy studovat.

Obrázek knihy označuje poznámku týkající se používané terminologie. Tato poznámka většinou upozorňuje na další používané termíny ozna-čující stejnou skutečnost. Seznam všech terminologických poznámek najdete v rejstříku pod heslem „terminologie“.

Page 17: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Úvod 17

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 17 z 433

Obrázek počítače označuje zadání úkolu, který máte samostatně vypra-covat.Seznam všech úloh najdete v rejstříku pod heslem „úloha“.

Píšící ruka označuje obyčejnou poznámku, ve které pouze doplňuji in-formace z hlavního proudu výkladu o nějakou zajímavost.

Ruka s hrozícím prstem upozorňuje na věci, které byste měli určitě vě-dět a na které byste si měli dát pozor, protože jejich zanedbání vás vět-šinou dostane do problémů.

☺ Usměváček vás bude upozorňovat na různé tipy, kterými můžete vy-lepšit svůj program nebo zefektivnit svoji práci.

Mračoun vás naopak bude upozorňovat na různá úskalí programova-cího jazyka nebo programů, s nimiž budeme pracovat, a bude vám ra-dit, jak se těmto nástrahám vyhnout či jak to zařídit, aby vám alespoň pokud možno nevadily.

Brýle označují tzv. „poznámky pro šťouraly“, ve kterých se vás snažím seznámit s některými zajímavými vlastnostmi probírané konstrukce nebo upozorňuji na některé souvislosti, avšak které nejsou k pochopení látky nezbytné.

Symbol znamení raka označuje poznámku, ve které poukazuji na inter-pretaci nějakého obratu či konstrukce v analogii ze světa robotů, kterou zavádím v kapitole Analogie na straně 43. Seznam všech odkazů na tuto analogii najdete v rejstříku pod heslem „analogie“.

Page 18: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 18 z 433

Část 1: Zapouzdření

Page 19: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 19

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 19 z 433

1. Seznamujeme se s nástroji

Kapitola 1 Seznamujeme se s nástroji

☯ Co se v kapitole naučíme V této kapitole se seznámíte s nástroji, které budete při studiu dalších částí knihy potřebovat. Nejprve vám povím o historii a současných trendech v programování a prozradím vám, proč jsem pro výuku vy-bral programovací jazyk Java. Pak vám ukážu vývojové prostředí BlueJa naznačím, jak je máte nainstalovat na svůj počítač. Poté vám vysvět-lím, jak jsou organizovány doprovodné projekty k učebnici a ukážu vám, jak je připravit, abyste s nimi mohli v průběhu studia učebnice snadno pracovat. Na závěr vám předvedu, jak otevřít projekt ve vývo-jovém prostředí BlueJ, prozradím vám, co je to diagram tříd a naučím vás, jak je možno tento diagram v prostředí BlueJ upravovat.

1.1 Trochu historie

První počítače Historie počítačů (tj. strojů, u nichž je postup výpočtu řízen programem) a pro-gramování se začala psát již v devatenáctém století. Charles Babbage tehdy dostal zakázku od anglického námořnictva na vylepšení svého počítacího stroje pro vý-počet navigačních tabulek. V roce 1848 dokončil návrh počítacího stroje, který byl řízený programem zadávaným na děrných štítcích. Zbytek života se pak věnoval jeho konstrukci. Stroj byl mechanický a měl být poháněn parním strojem, ale z programátorského hlediska již umožňoval značnou část operací, kterými se hono-sí současné počítače.

Page 20: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

20 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 20 z 433

Zajímavé je, že prvním programátorem byla žena – Ada Lovelance Augusta (mimochodem dcera známého romantického básníka lorda Byrona), která se s Ba-bbagem spřátelila a pomáhala mu s některými výpočty. Jednou se na výletě v Itá-lii setkala s italským poručíkem, který pro místní smetánku přednášel o Babbageově stroji. Aby Babbageův stroj zpopularizovala, přeložila přednášku, která popisovala hardware tohoto počítače, do angličtiny a na Babbageův popud překlad doplnila „poznámkami překladatele“, kde vysvětlila možnosti stroje ze softwarového hlediska. Svůj výklad doplnila i krátkým programem na výpočet Fibonacciho čísel – prvním publikovaným počítačovým programem na světě. (Na její počest byl v osmdesátých letech minulého století pojmenován programovací jazyk Ada.)

Babbageovi se jeho počítač nepodařilo rozchodit, protože vyžadoval součást-ky z materiálu s pevností pancéřové oceli opracované s hodinářskou přesností, a to bylo pro tehdejší technologii příliš velké sousto. Rozchodil jej až jeho vnuk, kte-rý byl americkým generálem, a při odchodu do penze se rozhodl, že všem ukáže, že dědeček nebyl blázen a jeho stroj by byl schopen provozu.

První funkční počítač postavil až v roce 1938 v Německu Konrád Zuse. Další počítače se objevily v průběhu druhé světové války. Nejslavnějším z nich byl ENIAC, který byl vyroben v roce 1944 a byl prvním čistě elektronickým počítačem (ostatní ještě používaly relé1 či dokonce mechanické prvky). Skutečné počítačové (a tím i programátorské) orgie však začaly až v padesátých letech, kdy se začaly počítače sériově vyrábět a počítačová věda (computer science) se zabydlela na všech univerzitách.

Program musí být především spolehlivý Počítače byly postupně nasazovány v dalších a dalších oblastech a programátoři pro ně vytvářeli dokonalejší a dokonalejší programy. Programy byly čím dál rafi-novanější a složitější a to začalo vyvolávat velké problémy. Programátoři totiž přestávali být schopni své programy rozchodit a když je vítězně rozchodili, nedo-kázali z nich v rozumném čase odstranit chyby, které uživatelé v programu obje-vili.

Tato krize vedla postupně k zavádění nejrůznějších metodik, které měly jedi-ný cíl: pomoci programátorům psát spolehlivé a snadno upravovatelné programy. V padesátých letech minulého století se tak prosadily vyšší programovací jazyky, v šedesátých letech modulové programování, v sedmdesátých letech na ně navá-

1 Relé je elektromechanický prvek, kde průchod proudu cívkou zapříčiní sepnutí nebo rozpo-

jení kontaktů. Rychlá relé dokázala sepnout i 100krát za sekundu – to byla také maximální rychlost tehdejších počítačů.

Page 21: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 21

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 21 z 433

zalo strukturované programování a v průběhu osmdesátých a zejména pak deva-desátých let ovládlo programátorský svět objektově orientované programování, jehož vláda pokračuje dodnes.

Hlavním cílem programátorů v počátcích programování bylo, aby jejich pro-gramy spotřebovaly co nejméně paměti a byly co nejrychlejší. Tehdejší počítače totiž měly paměti málo, byly velice pomalé a jejich strojový čas byl drahý. Se stoupající složitostí programů však byly takto psané programy stále méně stabilní a stále hůře udržovatelné. Současně s tím, jak klesala cena počítačů, jejich strojo-vého času i paměti, začínal být nejdražším článkem v celém vývoji člověk.

Cena strojového času a dalších prostředků spotřebovaných za dobu života programu začínala být pouze zlomkem ceny, kterou bylo nutno zaplatit za jeho návrh, zakódování, odladění a následnou údržbu. Začal být proto kladen stále větší důraz na produktivitu programátorů i za cenu snížení efektivity výsledného programu.

Prakticky každý program zaznamená během svého života řadu změn. Tak, jak se průběžně mění požadavky zákazníka na to, co má program umět, je pro-gram postupně upravován, rozšiřován a vylepšován. Celé současné programová-ní je proto vedeno snahou psát programy nejenom tak, aby pracovaly efektivně, tj. rychle a s minimální spotřebou různých zdrojů (operační paměť, prostor na disku, kapacita sítě atd.), ale aby je také bylo možno kdykoliv jednoduše upravit a vy-lepšit.

Předchozí zásady krásně shrnul Martin Fowler ve své knize Refactoring:

„Napsat program, kterému porozumí počítač, umí i hlupák. Dobrý programátor píše programy, kterým porozumí i člověk.“

Mějte při tvorbě svých programů tuto větu neustále na paměti.

1.2 Objektově orientované programování – OOP

Vývoj metodik programování Jak jsem řekl, v průběhu doby se prosadilo několik metodik, které doporučovaly, jak programovat, abychom byli s programem co nejdříve hotovi a výsledný pro-gram byl co nejkvalitnější.

Page 22: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

22 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 22 z 433

Modulární programování ukazovalo, že rychlost vývoje i kvalitu výsledné programu zvýšíme, když vhodně rozdělíme velký projekt do sady menších, ro-zumně samostatných modulů.

Strukturované programování se ponořilo do hloubky kódu a ukazovalo, že dalšího zvýšení produktivity vývoje i kvality výsledných programů dosáhneme dodržením několika jednoduchých zásad při vlastním psaní kódu.

Objektově orientované programování (v dalším textu budu využívat zkratku OOP) se naopak obrátilo do vyšších hladin a ukázalo, že vhodným zvýšením abs-trakce dokážeme rychle a spolehlivě navrhovat a vyvíjet ještě větší a komplikova-nější projekty.

Jednou z největších překážek v efektivní tvorbě kvalitních programů je tzv. sémantická mezera mezi tím, co chceme vytvořit a tím, co máme k dispozici. Naše programy mají řešit široké spektrum úkolů od řízení mikrovlnné trouby přes nej-různější kancelářské a grafické programy a hry až po složité vědecké úlohy, kos-mické lety či protivzdušnou obranu kontinentu. Ve svém rozletu jsme ale odkázáni na stroje, které si umějí pouze hrát s nulami a jedničkami. Čím budou naše vyjadřovací možnosti blíže zpracovávané skutečnosti, tím rychleji a lépe do-kážeme naše programy navrhnout, zprovoznit a udržovat.

Jinými slovy: Kvalita programu a rychlost jeho tvorby je velice úzce svázána s hladinou abstrakce, kterou při jejich tvorbě používáme. Budeme-li např. progra-movat ovládání robota, bude pro nás výhodnější programovací jazyk, v němž můžeme zadat příkazy typu „zvedni pravou ruku“, než jazyk, v němž musíme vše vyjadřovat pomocí strojových instrukcí typu „dej do registru A dvojku a ob-sah registru A pak pošli na port 27“.

OOP přichází s výrazovými prostředky, které nám umožňují maximálně zvý-šit hladinu abstrakce, na které se „bavíme“ s našimi programy a tím maximálně zmenšit onu sémantickou mezeru mezi tím, co máme k dispozici a co bychom po-třebovali.

Principy OOP Objektově orientované programování vychází z myšlenky, že všechny programy, které vytváříme, jsou simulací buď skutečného nebo nějakého námi vymyšleného virtuálního světa. Čím bude tato simulace přesnější, tím bude výsledný program lepší.

Všechny tyto simulované světy jsou ve skutečnosti světy objektů, které mají různé vlastnosti a schopnosti a které spolu nějakým způsobem komunikují. Ko-munikací se přitom nemyslí jen klasická komunikace mezi lidmi či zvířaty, ale i mnohem obecnější vzájemné interakce (např. židle a podlaha spolu komunikují

Page 23: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 23

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 23 z 433

tak, že židle stojí na podlaze a naopak podlaha podpírá židli, aby se skrz ní ne-probořila).

Budeme-li chtít, aby naše programy modelovaly tento svět co nejvěrněji, měly by být schopny modelovat obecné objekty spolu s jejich specifickými vlastnostmi a schopnostmi a současně modelovat i jejich vzájemnou komunikaci.

Máme-li být schopni rychle vytvářet kvalitní programy, měli bychom mít k dispozici jazyk, jehož jazykové konstrukce nám umožní se vyjadřovat co nejpřiro-zeněji a co nejméně se nechat znásilňovat omezeními počítače, na kterém má pro-gram běžet. Musí umožnit co nejvyšší míru abstrakce, při níž se můžeme vyjadřovat tak, abychom mohli přirozeně popsat modelovanou skutečnost.

Všechny moderní programovací jazyky se honosí přídomkem „objektově ori-entované“. Tím se nám snaží naznačit, že nabízejí konstrukce, které umožňují ro-zumně modelovat náš okolní, objekty tvořený svět (a nejen jej).

1.3 Překladače, interprety, platformy Tato podkapitola je určena těm, kteří se nespokojí jen s tím, že věci fungují, ale chtějí také vědět, jak fungují. Naznačíme si v ní, jak je to v počítači zařízeno, že programy pracují.

Operační systém a platforma Operační systém je podle definice sada programů, jejímž úkolem je zařídit, aby počítač co nejlépe sloužil zadanému účelu. Operační systémy osobních počítačů se snaží poskytnout co největší komfort a funkčnost jak lidským uživatelům, tak programům, které operační systém nebo tito uživatelé spouští. (Teď nehodnotím, jak se jim to daří.)

Operační systém se snaží uživatele odstínit od hardwaru použitého počítače. Uživatel může střídat počítače, avšak dokud bude na všech stejný operační sys-tém, bude si se všemi stejně rozumět.

Při obsluze lidského uživatele to má operační systém jednoduché: člověk komunikuje s počítačem pomocí klávesnice, obrazovky, myši a případně několika dalších zařízení. Ty všechny může operační systém převzít do své správy a za-bezpečit, aby se nejrůznější počítače chovaly vůči uživateli stejně.

U programů to má ale složitější. Programy totiž potřebují komunikovat neje-nom s operačním systémem (např. když chtějí něco přečíst z disku nebo na něj zapsat), ale také přímo s procesorem, kterému potřebují předat své instrukce k vykonání. Problémem ale je, že různé procesory rozumí různým sadám instrukcí.

Page 24: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

24 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 24 z 433

Abychom věděli, že náš program na počítači správně poběží, musíme vědět, že počítač bude rozumět té správné sadě instrukcí a že na něm poběží ten správný operační systém. Kombinaci operační systém + procesor budeme v dalším textu označovat jako platforma.

Nejrozšířenější platformou současnosti je operační systém Windows na počí-tačích s procesory kompatibilními s procesorem Intel Pentium. Další vám známou platformou bude nejspíš operační systém Linux na počítačích s procesory kompa-tibilními s Pentiem. Oba zmíněné operační systémy však mají své verze běžící na počítačích s jinými procesory.

Programovací jazyky Jak asi všichni víte, pro zápis programů používáme nejrůznější programovací ja-zyky. Ty jsou vymýšleny tak, aby v nich mohl člověk co nejlépe popsat svoji před-stavu o tom, jak má počítač splnit požadovanou úlohu.

Program zapsaný v programovacím jazyku pak musíme nějakým způsobem převést do podoby, které porozumí počítač. Podle způsobu, jakým postupujme, dělíme programy na překládané a interpretované.

U překládaných programů se musí napsaný program nejprve předat zvlášt-ním programu nazývanému překladač (někdo dává přednost termínu kompilátor), který jej přeloží (zkompiluje), tj. převede jej do podoby, s níž si již daná platforma ví rady, tj. musí jej přeložit do kódu příslušného procesoru a pou-žívat instrukce, kterým rozumí použitý operační systém. Přeložený program pak můžeme kdykoliv na požádání spustit.

Naproti tomu interpretovaný program předáváme v podobě, v jaké jej pro-gramátor vytvořil, programu označovanému jako interpret. Ten obdržený pro-gram prochází a ihned jej také provádí.

Výhodou překládaných programů je, že většinou běží výrazně rychleji, protože u interpretovaných programů musí interpret vždy nejprve přečíst kus programu, zjistit, co má udělat a teprve pak může tento požadavek vykonat.

Výhodou interpretovaných programů bývá na druhou stranu to, že jim větši-nou tak moc nezáleží na tom, na jaké platformě běží. Stačí, když na daném počíta-či běží potřebný interpret. Naproti tomu překládané programy se většinou musí pro každou platformu trochu (nebo také hodně) upravit a znovu přeložit.

Vedle těchto základních druhů programů existují ještě hybridní programy, které jsou současně překládané i interpretované a které se snaží sloučit výhody obou skupin. Hybridní program se nejprve přeloží do jakéhosi mezijazyka, který je vymyšlen tak, aby jej bylo možno co nejrychleji interpretovat. Takto přeložený

Page 25: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 25

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 25 z 433

program je potom interpretován speciálním interpretem označovaným často jako virtuální stroj1.

Chytřejší verze těchto interpretů (virtuálních strojů) dokonce dokáží odhalit často se opakující části kódu a někam stranou je přeložit, aby je nemusely pořád kolem dokola interpretovat. Program pak může běžet skoro stejně rychle jako překládaný (a někdy i rychleji).

Hybridní programy spojují výhody obou kategorií. K tomu, aby v nich na-psané programy mohly běžet na různých platformách, stačí pro každou platformu vyvinout potřebný interpret – virtuální stroj. Je-li pak tento virtuální stroj dosta-tečně „chytrý“ (a to jsou v současné době prakticky všechny), běží program téměř stejně rychle, jako kdyby byl přeložený.

Na překládané, interpretované a hybridní bychom měli dělit programy,avšak často se takto dělí i programovací jazyky. Je sice pravda, že to, zda bude program překládaný, interpretovaný nebo hybridní není zá-vislé na použitém jazyku, ale je to především záležitostí implementace daného jazyka, nicméně každý z jazyků má svoji typickou implementa-ci, podle které je pak zařazován.

Prakticky všechny jazyky sice mohou být implementovány všemitřemi způsoby a řada jich opravdu ve všech třech podobách existuje(jako příklad bychom mohli uvést jazyky Basic, Java nebo Pascal), ale u většiny převažuje typická implementace natolik výrazně, že se o těch ostatních prakticky nemluví. Z vyjmenované trojice je např. klasickýBasic považován za interpretovaný jazyk, Java za hybridní a Pascal zapřekládaný.

Hybridní implementace jazyků se v posledních letech výrazně prosadily a jsou dnes králi programátorského světa. Vyvíjí v nich převážná většina programátorů a procento implementací v těchto jazycích neustále vzrůstá.

1.4 Java a její zvláštnosti O splnění zásad objektově orientovaného programování se snaží tzv. objektově orientované jazyky. Mezi nimi je v současné době nejpopulárnější programovací jazyk Java, který se narodil v roce 1995. Hned po svém vzniku zaznamenal velký

1 Virtuální stroj se mu říká proto, že se vůči programu v mezijazyku chová obdobně, jako se

chová procesor vůči programu v čistém strojovém kódu.

Page 26: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

26 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 26 z 433

ohlas a během několika let v něm začalo vyvíjet své programy více než 3 milióny programátorů a toto číslo stále stoupá. V roce 2003 se podle průzkumů renomo-vaných společností stal nejpoužívanějším programovacím jazykem. Na převážné většině univerzit se Java používá jako vstupní jazyk pro výuku programování.

Jaké jsou vlastnosti tohoto programovacího jazyka, že v tak krátké chvíli tak významně ovlivnil programátorský svět? Důvodů je celá řada. Zde uvedu jen ty z nich, které se výhodně projeví při výuce programování.

Objektově orientovaná Java plně podporuje objektově orientované programování. Na rozdíl od C++ a dalších jazyků „klasického ražení“ již neumožňuje napsat program, který by ne-byl objektově orientovaný. Její autoři se přitom do ní snažili začlenit převážnou většinu zásad objektově orientovaného programování.

Předchozí tvrzení musím trochu zlehčit. Platí obecná zásada, že jako čuně mohu programovat v jakémkoliv programovacím jazyce. Java (a s ní i dalších moderní programovací jazyky) nedovolují zapsat program, který by nebyl alespoň formálně objektově orientovaný. Bude-li objek-tově orientovaný doopravdy, tj. i svou architekturou a duchem, to už záleží na programátorovi. To za vás žádný programovací jazyk neudě-lá.

Jednoduchá Java je velice jednoduchý jazyk. Základy jeho syntaxe může člověk, který umí programovat, zvládnout během několika hodin. Pak už se může soustředit na po-znání a pochopení funkce klíčových knihoven a na jejich co nejefektivnější využití.

Jednoduchost jazyka však neznamená jeho omezenost. Jak jsem již řekl, Java je v současnosti nejpoužívanějším programovacím jazykem. Programují se v ní aplikace všech rozměrů od drobných programů, které se umísťují na čipové karty, přes programy pro mobilní telefony, desktopové aplikace až po obří projekty roz-prostřené na řadě vzájemně komunikujících počítačů.

Překládaná i interpretovaná Java je současně překládaná i interpretovaná (řadí se proto mezi hybridní pro-gramovací jazyky). Programy v jazyku Java se nejprve přeloží do speciálního tva-ru nazývaného bajtkód, který pak analyzuje a interpretuje speciální program nazývaný virtuální stroj Javy (Java Virtual Machine – používá se pro něj zkratka

Page 27: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 27

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 27 z 433

VM). Úkolem virtuálního stroje je nejenom vykonat příslušný program, ale také tento program „odstínit“ od hardwaru a operačního systému, na kterém program běží.

Cílem překladu do bajtkódu je převést program do podoby, s níž se bude vir-tuálnímu stroji co nejlépe pracovat. Současné virtuální stroje jsou dokonce natolik inteligentní, že umí provádění programu za běhu optimalizovat, takže program na nich běží skoro tak rychle, jako kdyby byl přeložený, a v některých speciálních případech dokonce rychleji.

Multiplatformní Jedním z klíčových záměrů autorů Javy bylo vytvořit nejenom jazyk, ale celou platformu (= prostředí, ve kterém běhají programy). Tuto platformu realizuje vir-tuální stroj spolu se základní knihovnou nejpoužívanějších funkcí.

Virtuální stroj umožňuje, aby jeden a týž program běhal na různých počíta-čích a operačních systémech. Pro každou konfiguraci hardwaru a operačního sys-tému lze definovat její vlastní virtuální stroj. Ten se pak stará o správný běh programů, takže se program (a s ním i uživatel) vůbec nemusí starat o to, na ja-kém hardwaru a operačním systému zrovna běží.

Java běhá pod systémy Windows, Unix, Linux, MacOS, Solaris a řadou dal-ších operačních systémů. Vytvoříte-li program v Javě, můžete jej spustit téměř na libovolném počítači. Jak mnozí z vás ví, programy v Javě je možné spouštět i na řadě mobilních telefonů a dokonce ovládají i miliony čipových karet.

Java je totiž nejenom programovací jazyk, ale také platforma. Tímto termí-nem označujeme soubor programů, které se vůči programům běžícím na dané platformě chovají obdobně jako operační systém – odstiňují je od detailů použité-ho hardwaru a operačního systému. Kdykoliv tyto programy po systému něco chtějí, požádají o to platformu a ta jim příslušné služby zprostředkuje. Takovým programům pak může být jedno, pod jakým operačním systémem běží, protože se beztak „baví“ pouze s platformou.

Java nabízí dvě verze platformy. Jednodušší JRE (Java Runtime Environment – běhové prostředí Javy) poskytuje vše potřebné pro běh programů. Komplexnější SDK (Software Development Kit – sada nástrojů pro vývoj softwaru), označovaná někdy také JDK (Java Development Kit) je vlastně JRE doplněné o základní vývo-jové nástroje (překladač, generátor dokumentace, ladící program a další) a posky-tuje tak vše potřebné pro vývoj programů.

Vy se chcete naučit pomocí této učebnice programovat, tak budete potřebovat mít instalované SDK. Uživatelům, kteří pak budou spouštět vaše programy, stačí JRE.

Page 28: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

28 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 28 z 433

Obě verze si můžete stáhnout a instalovat zdarma. Postup instalace tohoto prostředí je vysvětlen v příloze Instalace vývojového prostředí na straně 407. V dal-ším textu budu předpokládat, že máte SDK nainstalováno.

1.5 Vývojové prostředí BlueJ Mnozí autoři učebnic vystačí při výkladu s nástroji z SDK doplněnými o nějaký editor, ve kterém píší zdrojové texty programů. Obdobným způsobem pracuje i část programátorů (podle některých průzkumů takto pracuje asi 7 % programáto-rů). Většina programátorů však dává přednost speciálním vývojovým prostředím označovaným souhrnně zkratkou IDE – Integrated Development Environment (integrované vývojové prostředí), což je sada programů, která nabízí řadu funkcí, jež vývoj programů výrazně zefektivňují.

IDE dáme přednost i my. V této učebnici budu používat vývojové prostředí BlueJ (čtěte blúdžej), které bylo vyvinuté speciálně pro výuku objektově oriento-vaného programování a oproti jiným IDE nabízí několik vlastností, jež se nám budou při výkladu základů OOP hodit.

Je maximálně jednoduché, takže se začátečníci mohou soustředit na své pro-gramy a nejsou rozptylováni záplavou možností, kterými je rozptylují profe-sionální vývojová prostředí a které na počátku beztak neumí využít.

Je názorné, protože slučuje možnosti klasického textového zápisu programů s možností definice jeho architektury v grafickém prostředí. Tato koncepce se stává základním postupem návrhu programů. BlueJ je prozatím jediné pro-středí, které je k dispozici zdarma a přitom je schopno vytvořit na základě grafického návrhu kostru programu a průběžně zanášet změny v programu do jeho grafické podoby a naopak změny v grafickém návrhu do textové po-doby programu, aniž by ovlivnilo ty části programu, které programátor zadal „ručně“.

Je interaktivní – umožňuje přímou práci s objekty. Ostatní prostředí vám vět-šinou povolují pouze vytvořit program, který můžete spustit. Přímou komu-nikaci s jednotlivými součástmi programu, tj. vytváření objektů a zasílání zpráv řízené interaktivně uživatelem, však většinou neumožňují.

Toto prostředí se používá ve vstupních kurzech programování na stovkách uni-verzit a školících středisek po celém světě (v létě 2004 se k používání tohoto pro-středí hlásilo okolo 400 univerzit a školících center). Jak se můžete přesvědčit na stránkách http://www.BlueJ.org, jeho výhodné vlastnosti přivedly řadu programá-

Page 29: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 29

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 29 z 433

torů k tomu, že je začali používat i pro vývoj menších profesionálních aplikací. Ti-to programátoři oceňují zejména jeho malou paměťovou náročnost (BlueJ vystačí se 48 MB RAM, zatímco běžná profesionální vývojová prostředí požadují mini-málně 256 MB RAM) a originální, jinde nenabízené možnosti interaktivního ladě-ní programů.

Stejně jako SDK, i vývojové prostředí BlueJ si můžete stáhnout a instalovat zdarma. Vše potřebné pro jeho instalaci se dozvíte v příloze Instalace prostředí BlueJ na straně 409. V dalším textu budu předpokládat, že máte toto prostředí na-instalováno spolu s rozšířením, o němž se v této kapitole zmiňuji.

1.6 Projekty a BlueJ V současných vývojových prostředích již nepracujeme s programy, ale s projekty. Projekt může obsahovat jeden program (tj. něco, co spustíme a ono to běží) nebo několik programů – to podle toho, jak se nám to zrovna hodí.

Typickým příkladem jednoduchého projektu sestávajícího z několika programů byl vítězný projekt mladší kategorie z programátorské sou-těže BB20021. Byl jím soubor deskových her, který obsahoval 5 progra-mů: hry Dáma a Reversi vytvořené v programovacím jazyku Baltík, hru Piškvorky vytvořenou v jazyku Baltazar, hlavní program umožňující volbu hry vytvořený opět v jazyku Baltík a skript umožňující přepínání programů vytvořený v jazyku JavaScript.

Jednotlivé hry byly v případě potřeby spustitelné samostatně. Jejich sloučením do většího celku a doplněním o nadstavbové uživatelské rozhraní pro výběr hry vzniklo takové malé herní centrum, které mělo pro řadu uživatelů větší užitnou hodnotu, než sada samostatných, na sobě nezávislých her.

Umístění projektů na disku Doporučuji vám, abyste si pro projekty zřídili novou složku. Do ní pak budete umisťovat jak svoje projekty, tak projekty, které budou součástí tohoto seriálu.

Jednou z možností je zřídit na datovém disku složku Java s podsložkami Texty a Projekty. Do složky Texty si můžete vložit texty týkající se programování a Javy, ve

1 Program si můžete stáhnout na stránkách www.sgp.cz.

Page 30: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

30 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 30 z 433

složce Projekty si pak zřiďte pro každý projekt novou složku, ve které budou umís-těny všechny soubory, které k němu budou patřit.

Jakmile složku pro své projekty zřídíte, můžete do ní hned stáhnout projekty k této učebnici, které najdete na adrese http://vyuka.pecinovsky.cz a které jsou stručně popsány v příloze Použité projekty na straně 417.

Windows a substituované disky V operačním systému Windows můžete používat 26 logických disků – pro každé písmeno abecedy jeden. Většina uživatelů však používá pouze zlomek tohoto počtu. Disky A: a B: bývaly donedávna vyhrazeny pro mechaniky pružných disků, na disku C: má většina uživatelů operační systém. Pak na počítači nejdete ještě pár dalších jednotek vyhrazených pro další oddíly vel-kého disku, pro CD-ROM a případně ještě nějaké síťové disky či zařízení, kte-rá se vůči počítači tváří jako další disk (např. paměť Flash-RAM) a tím výčet končí. Průměrný uživatel tak má většinu písmen abecedy nevyužitých.

Operační systém umožňuje použít tyto názvy pro tzv. substituované disky, což jsou složky, které se rozhodnete vydávat za logický disk. Protože o této možnosti většina uživatelů neví a přitom je to funkce velice užitečná,ukážu vám, jak ji můžete využít.

Substituované disky se definují pomocí příkazu

SUBST název_disku substituovaná_složka

Nejjednodušší způsob, jak definovat ve Windows např. substituovaný disk J:, je vložit do složky, kterou budete chtít substituovat jako disk J:, dávkový soubor s příkazem k substituci. (Písmeno J se pro Javu hodí nejlépe, ale mů-žete si vybrat jakékoliv jiné.)

Pokud jste ještě nepracovali s dávkovými soubory, tak vězte, že to jsou obyčejné textové soubory, do nichž zapisujete příkazy pro operační systém.Jejich název může být libovolný, ale musí mít příponu bat (zkratka ze slova batch – dávka).

V dávkovém souboru budou následujícím příkazy (na velikosti písmen nezáleží):

SUBST J: /d SUBST J: .

První příkaz má za úkol zrušit případnou doposud nastavenou substitucidisku J: (pokud v daném okamžiku takový disk není, systém vypíše chybo-vou zprávu, ale jinak se nic nestane), druhý příkaz pak substituuje aktuálnísložku jako disk J:.

Kdykoliv budete od této chvíle chtít substituovat složku, do níž jste dáv-

Page 31: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 31

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 31 z 433

kový soubor umístili, jako zadaný disk, spustíte příslušný dávkový soubor obdobně, jako byste spouštěli aplikaci – tj. např. poklepáním na jeho ikonu v Průzkumníku.

Budete-li chtít mít danou substituci nastavenu trvale, můžete umístit zá-stupce dávkového souboru do startovací nabídky do složky Start → Programy →Po spuštění a systém spustí příslušnou dávku po každém restartu počítače.

Po spuštění příslušného dávkového souboru (spouští se poklepáním jako jakýkoliv jiný program nebo skript) se rozšíří používané disky o právě substi-tuovaný disk – v našem případě o disk J:.

Kdykoliv od této chvíle budete pracovat s diskem J:, budete ve skuteč-nosti pracovat s obsahem substituované složky. A naopak: cokoliv uděláte s obsahem substituované složky, uděláte zároveň s obsahem disku J:.

Abyste mohli složku substituovat jako nějaký disk, nesmí váš operačnísystém používat disk označený tímto písmenem pro nějaké hardwarové zaří-zení (např. pevný disk, CD-ROM, Flash-disk apod.). Písmeno může být pou-žito nejvýše pro jiný substituovaný disk, protože tuto substituci můžete před nastavením nové substituce zrušit (pro tento případ je v dávkovém souboru první příkaz).

Substituované složky umožňují sjednotit prostředí několika počítačů. Tuto knihu např. připravuji na několika počítačích, přičemž každý z nich má jinak uspořádané složky (vadí mi to, ale nemohu s tím nic dělat). V každém z nich jsou ale definovány následující substituované disky:

J: pro složku s vývojovými nástroji Javy,

S: pro složku, ve které je text knihy,

V: pro složku, ve které jsou programy pro knihu.

Po této úpravě mi již nutnost přecházení mezi různými počítači nevadí, pro-tože vím, že na každém z nich najdu potřebné nástroje a dokumentaci na dis-cích J:, S: a V:.

Používáte-li operační systém Windows, můžete urychlit budoucí vyhle-dání složky s projekty právě tím, že pro ni zřídíte zvláštní substituovaný disk. Kdykoliv se pak obrátíte na příslušný disk, obrátíte se ve skutečnostik příslušné složce. Pomocí substituce si tak můžete zkrátit cestu k často pou-žívaným složkám.

Page 32: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

32 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 32 z 433

Vyhledání a otevření projektu

V následujícím textu budeme pracovat s projektem 02_Objekty, který je určen pro první seznámení s prostředím BlueJ, třídami a objekty a s nímž budeme pracovat celou příští kapitolu.

Předpokládám, že máte instalováno prostředí BlueJ a stažené doprovodné projek-ty k učebnici (připomínám, že postup je popsán v příloze Instalace prostředí BlueJ na straně 409). Spusťte BlueJ, zadejte příkaz Projekt → Otevřít. Po zadání příkazu se otevře dialogové okno Otevřít projekt, v němž vyhledáte a označíte složku s projek-tem a stisknete tlačítko Otevřít (viz obr. 1.1).

Obrázek 1.1 Otevření existujícího projektu

Na obrázku si všimněte, že ikony projektů (přesněji ikony složek, v nichž je ulo-žen projekt) vypadají jinak než ikony obyčejných složek. BlueJ totiž do složky, ve které budou soubory jeho projektu, přidá svůj vlastní soubor bluej.pkg, do nějž si ukládá informace o grafickém uspořádání objektů v zobrazovaném diagramu. Podle přítomnosti tohoto souboru pak pozná, zda se jedná o složku s jeho projek-tem nebo o nějakou obyčejnou složku.

Najděte složku s projektem 02_Objekty, klepněte na ni a své zadání potvrďte stiskem tlačítka Otevřít. Po otevření projektu by okno BlueJ mělo vypadat obdobně jako na obrázku 1.2.

Page 33: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 33

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 33 z 433

Obrázek 1.2 Okno BlueJ po otevření projektu 02_Objekty

1.7 Diagram tříd Velký obdélník zabírající většinu okna projektu obsahuje tzv. diagram tříd našeho projektu. Diagram tříd popisuje strukturu našeho programu a vzájemné vazby mezi jeho částmi.

Malé obdélníky v diagramu tříd představují části programu, které nazýváme třídy (za chvíli si o nich budeme povídat podrobněji). Jsou znázorněny podle konvencí grafického jazyka UML1 (vybarvení obdélníků do konvence nepatří, ale zvyšuje přehlednost a názornost diagramu). Čárkované šipky prozrazují, kdo ko-ho používá – např. šipky vedoucí od tříd Obdélník, Trojúhelník a Elipsa ke třídě Plátno naznačují, že tyto třídy třídu Plátno používají (jak se vzápětí dozvíte, tyto obrazce se na plátno kreslí). 1 UML je zkratkou z anglického Unified Modeling Language – sjednocený modelovací jazyk.

Je to grafický jazyk, ve kterém programátoři navrhují své aplikace před tím, než začnou psát program. Více se o tomto jazyku můžete dozvědět např. v knize XXX: Myslíme v jazyku UML, Grada, 2002 (ISBN 80-7169-).

Page 34: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

34 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 34 z 433

Bílý obrázek listu papíru v levém horním rohu diagramu představuje textový soubor se základním popisem celého projektu. Když na něj poklepete, otevře se okno editoru, v němž si budete moci tyto informace přečíst a v případě potřeby je i upravit či doplnit.

V doprovodných programech k učebnici se snažím důsledně používat české názvy. Jak si zanedlouho prozradíme, Java s nimi nemá problé-my. Ty však nastanou v okamžiku, kdy se rozhodneme přenést soubo-ry z jednoho operačního systému na druhý. Operační systémy totiž prozatím nemají sjednocené kódování znaků s diakritikou. To se proje-vuje zejména v názvech souborů. Název souboru zakódovaný pod jed-ním operačním systémem nemusí jít pod druhým operačním systémem přečíst.

Pro ty, kteří budou mít na svém počítači s diakritikou problémy (a pro ty, kteří s používáním diakritiky zásadně nesouhlasí), je na webu připravena druhá sada, jejíž programy diakritiku nepoužívají.

Abyste si mohli navíc převést do „nediakritického“ tvaru i jiné pro-gramy, je pro vás na webu připraven program Odhackuj (jeho název je schválně bez diakritiky), který zkopíruje soubory do zadaného adresáře a přitom zbaví jejich názvy i jejich obsah veškeré diakritiky. Tento pro-gram je napsaný v Javě, takže by měl chodit na všech počítačích.

Šrafování spodní části obdélníků symbolizuje to, že třídy ještě nejsou přeloženy do bajtkódu. To lze snadno napravit: stiskněte tlačítko Přeložit v levé části aplikač-ního okna. Uvidíte, jak jedna třída za druhou ztmavne (tím prostředí naznačuje, že se překládá), aby pak opět zesvětlela a její šrafování zmizelo (již je přeložena). Od této chvíle můžete třídu používat.

Manipulace s třídami v diagramu Polohu jednotlivých tříd v diagramu můžete měnit. Najedete-li ukazatelem myši na obdélník představující danou třídu, změní se podoba kurzoru ze šipky na uka-zující ruku. V tomto okamžiku můžete obdélník uchopit (tj. stisknout a podržet primární [většinou levé] tlačítko myši) a přesunout jej do požadované pozice, kde jej upustíte (pustíte tlačítko myši).

Po stisku tlačítka myši rám obdélníku ztuční. V průběhu přesunu se s ukaza-telem myši bude přesouvat pouze tento rám, takže budete průběžně znát původní i nově nastavovanou pozici obdélníku (viz obrázek 1.3).

Page 35: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 35

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 35 z 433

Obrázek 1.3 Posunutí zástupce třídy v diagramu

Jsou-li zástupci tříd navzájem spojeni šipkami, bude BlueJ vaše přesuny sledovat a po umístění obrázku třídy do nové pozice šipky vždy příslušně překreslí. Můžete si tak diagram upravit do podoby, v níž vám bude připadat nejpřehlednější a nej-názornější.

Obdélníky zastupující třídy můžete nejenom přesouvat, ale můžete měnit i je-jich rozměr. Všimněte si, že poté, co na obdélník klepnete, objeví se u pravého dolního rohu ztučnělého rámu dvojité přeškrtnutí. Najedete-li ukazatelem myši do oblasti označené tímto přeškrtnutím, změní se jeho podoba na dvojitou šikmou šipku. Nyní můžete uchopit roh obdélníku, přesunout jej do nové pozice a upravit tak velikost obdélníku (viz obrázek 1.4).

Page 36: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

36 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 36 z 433

Obrázek 1.4 Změna velikosti zástupce třídy v diagramu

Někdy se stane, že se nám projekt rozrůstá a rozrůstá a najednou bychom potře-bovali posunout více tříd najednou, abychom udělali místo pro třídy, které se chystáme do projektu přidat. Postup je jednoduchý, ale nejprve se musíme naučit vybírat skupiny tříd.

Třídy jde zařazovat a vyřazovat ze skupiny vybraných tříd několika způsoby, které můžete kombinovat:

Stisknete přeřaďovač CTRL nebo SHIFT (je to jedno) a klepete postupně na ob-délníky, které chcete zahrnout od výběru.

Najedete ukazatelem myši do volného prostoru a stisknete tlačítko myši. Při stisknutém tlačítku pak popojedete ukazatelem a za ním se roztáhne výběro-vý podšeděný obdélník. Která třída do něj padne, ta se zařadí mezi vybrané (viz obr. 1.5).

Page 37: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 37

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 37 z 433

Obrázek 1.5 Výběr skupiny tříd pomocí „výběrového obdélníku“

Stisknete-li před předchozí operací přeřaďovač CTRL nebo SHIFT, budou se třídy „zasažené výběrovým obdélníkem“ přidávat ke třídám dříve vybra-ným.

Někdy toho vyberete víc, než potřebujete, a potřebujete některé třídy z výběru vy-jmout. Pak máte dvě možnosti:

Klepnutím myší do volného prostoru zrušíte výběr všech tříd.

Klepnutím na třídu při stisknutém přeřaďovači CTRL nebo SHIFT změníte vý-běr dané třídy – vybranou „odvyberete“ a nevybranou vyberete.

Celou operaci přesunu skupiny tříd si můžeme vyzkoušet:

1. Roztáhněte trochu okno projektu, aby bylo kam třídy přesunout.

2. Vyberte třídy tak, jak je naznačeno na obr. 1.5.

3. Najeďte na některou z vybraných tříd tak, aby se ukazatel myši změnil na ukazující ruku (předpokládám standardní nastavení podoby ukazatelů).

4. Uchopte myší tuto třídy (a s ní i ostatní vybrané) a přesuňte je do požadova-né cílové pozice – viz obr. 1.6.

Page 38: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

38 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 38 z 433

Obrázek 1.6 Přesun bloku tříd

1.8 Shrnutí – co jsme se naučili U programů je nejdůležitější jejich spolehlivost a snadná udržovatelnost.

Každý program je jakousi simulací reálného nebo virtuálního světa.

Efektivita programování a robustnost vyvinutých programů je silně ovlivně-na mírou abstrakce použité při jejich tvorbě. Čím může být naše vyjadřování blíže modelované skutečnosti, tím lépe se nám podaří napsat správný, spo-lehlivý a robustní program.

Moderní, objektově orientované jazyky se snaží zohlednit skutečnost, že jsou modelem světa tvořeného vzájemně interagujícími objekty.

Java je moderní programovací jazyk, který je jednoduchý, objektově oriento-vaný, přenositelný a multiplatformní

Při vývoji programů se většinou používají speciální vývojová prostředí ozna-čovaná zkratkou IDE.

Page 39: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 1: Seznamujeme se s nástroji 39

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 39 z 433

V této učebnici budeme používat vývojové prostředí BlueJ.

Současná vývojová prostředí organizují vyvíjené programy do tzv. projektů.

Strukturu programu můžeme znázornit v diagramu tříd.

Při znázorňování struktury programu používáme grafický jazyk UML.

Třídy jsou v diagramu tříd znázorněny rozdělenými obdélníky, v jejichž hor-ní části je název třídy. Spodní část je v BlueJ prázdná.

Prostředí BlueJ nám umožňuje měnit jejich umístění i velikost obdélníků, představujících v diagramu tříd jednotlivé třídy.

Nově zavedené termíny bajtkód BlueJ IDE Interpret Java JDK JRE OOP Projekt Program Překladač SDK UML Vývojové prostředí

Page 40: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

40 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 40 z 433

2. Pracujeme s třídami a objekty

Kapitola 2 Pracujeme s třídami a objekty

☯ Co se v kapitole naučíme Tato kapitola předpokládá, že již máte otevřen projekt 02_Objekty, o kte-rém jsme se bavili v minulé kapitole. Nejprve si povíme, co to vlastně ty třídy a objekty jsou a seznámíme se s novým termínem instance. Pak se s nimi naučíme pracovat v prostředí BlueJ. Vysvětlíme si, jak se objekty vytvářejí, jak reagují na zprávy, co to jsou jejich atributy a že vedle zpráv posílaných instancím existují i zprávy posílané celé třídě. Naučíte se vytvářet instance tříd, posílat jim zprávy a prohlížet si hodnoty jejich atributů.

2.1 Nejprve trocha teorie Než se vrhneme na vlastní programování, povíme si nejprve trochu o nejdůleži-tějších termínech, s nimiž se budeme v dalším výkladu setkávat. Abych vás tou teorií příliš neunavoval, tak tyto termíny opravdu jen zavedu a nebudu je nijak rozpitvávat – povím vám pouze to, co budete potřebovat vědět pro porozumění textu této a následující kapitoly. K řadě z nich se pak ještě vrátíme a vysvětlíme si je podrobněji i s ukázkami na příkladech.

Page 41: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 41

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 41 z 433

Třídy a jejich instance Jak jsem již naznačil, výchozím bodem celého objektově orientovaného progra-mování je tvrzení, že svět je světem objektů, které spolu nějak komunikují. Objek-ty, s nimiž se ve svém okolí setkáváme, můžeme rozdělit do skupin, které mají nějaké společné vlastnosti a které v programování označujeme jako třídy. Vlastní objekty pak označujeme jako instance příslušné třídy – např. židle, na které sedí-te, je instancí třídy židlí. Mohli bychom tedy říci, že instance (objekt) je nějakou konkrétní realizací své třídy (židle, na které sedím, je konkrétní realizací obecné židle).

Pojem objekt a instance jsou ve skutečnosti synonyma, která můžete s klidným svědomím zaměnit. Termínu objekt se dává většinou před-nost tehdy, hovoříme-li o obecných objektech, kdežto termínu instance dáváme přednost v situacích, kdy chceme zdůraznit, do jaké třídy ob-jekty, o nichž hovoříme, patří.

Třídy popisují společné vlastnosti svých instancí a definují také nástroje pro jejich vytváření. Přirovnáme-li programování k hraní si na pískovišti, pak třída je něco jako formička a její instance jsou bábovičky, které za pomoci této formičky vytvá-říme. Při jiném přirovnání bychom mohli prohlásit, že třída je vlastně továrna na objekty – své instance.

Třída může mít obecně libovolný počet instancí. Existují však i třídy, které dovolí vytvoření pouze omezeného počtu instancí, někdy dokonce povolí jen je-dinou instanci – jedináčka. Nevyskytují se příliš často, ale na druhou stranou nejsou žádnou exotickou výjimkou (s jednou se potkáme hned v prvním projek-tu).

Zprávy Objekty navzájem komunikují. Objektové programování hovoří o tom, že si objek-ty navzájem posílají zprávy, ve kterých se žádají o různé služby, přičemž onou službou může být často jen informace. Můžeme se např. židle zeptat, je-li čalou-něná. V praxi bychom to realizovali nejspíše tak, že bychom k ní poslali upřený pohled, v programu k ní vyšleme zprávu.

Na dotaz, co je to program, většinou odpovídám, že je to předpis, jak splnit zadanou úlohu, zapsaný v nějakém programovacím jazyce (dokud není zapsaný v programovacím jazyce, není to program, ale algoritmus). Tato definice je možná přesná, nicméně je tak obecná, že je pro naše účely prakticky nepoužitelná. Defi-

Page 42: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

42 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 42 z 433

nujme si proto, že objektově orientovaný program je v nějakém programovacím jazyce zapsaný popis použitých tříd, objektů a zpráv, které si tyto objekty posí-lají, doplněný u složitějších programů ještě o popis umístění programů na jed-notlivých počítačích a jejich svěření do správy příslušných služebních programů (např. operačních systémů či aplikačních serverů).

Budete-li slyšet programátory velkých aplikací hovořit o „deploymen-tu“, tak vězte, že hovoří o výše zmíněném rozmísťovacím aspektu svých programů.

Předchozí definici jsem uváděl proto, aby si ti, kteří mají nějaké zkušenosti s kla-sickým programováním, uvědomili, že u objektově orientovaného programování vstupují do trochu jiného programátorského vesmíru, než ve kterém se pohybují ti, kteří programují klasicky. Do vesmíru, v němž sice budou nadále používat mnohé z toho, co používá klasické, neobjektové programování, ale v němž se zá-kladní úvahy o koncepci a konstrukci programu ubírají naprosto jinými cestami, než na které byli doposud zvyklí.

Metody

Zde se objevuje drobný terminologický guláš. Java převzala terminolo-gii jazyka C++ a nehovoří o posílání zpráv, s nímž přišli zakladatelé ob-jektově orientovaného programování, ale o volání metod, které je bližší programátorům pracujícím v klasických jazycích.

Pokud jste již někdy programovali, tak pro ty z vás, kteří programo-vali v Baltíkovi, budou metody něco podobného jako Baltíkovi pomoc-níci, ti, kteří programovali v jiných jazycích, je mohou považovat ze ekvivalenty procedur a funkcí.

Metoda je část programu, kterou instance spustí v reakci na obdržení zprávy. Každá zpráva má v programu přiřazenu svoji vlastní metodu. Programátor v me-todě definuje, jak bude objekt na příslušnou zprávu reagovat.

Svým způsobem je jedno, jestli řeknete, že instanci pošlete zprávu nebo že zavoláte její metodu. Termín zpráva budu proto používat spíš v souvislosti s popi-sem chování programu (tj. prakticky celou tuto kapitolu), termínu metoda pak bu-du dávat přednost ve chvíli, kdy budu hovořit o konkrétní části programu realizující odpověď na zaslání příslušné zprávy. Jak jsem ale řekl, oba termíny jsou si svým způsobem ekvivalentní.

Page 43: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 43

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 43 z 433

2.2 Analogie Prozatím jsme se o třídách, objektech, zprávách a atributech bavili jako o něčem abstraktním, co se sice projevuje způsobem, který dokážeme vnímat, ale ne vždy jej také pochopit. Zkusím vám proto vše připodobnit k něčemu hmatatelnému – třeba tato představa některým z vás pomůže při chápání některých konstrukcí, které budeme ve zbytku knihy vytvářet.

Nepatříte-li k těm, kteří mají rádi teoretické koncepty ilustrované analogiemi z reálnějšího světa a domníváte-li se naopak, že by taková analogie vše jen zatem-nila, klidně tuto podkapitolu přeskočte.

Představte si svůj projekt jako město robotů. Každá třída v něm vystupuje ja-ko továrna vyrábějící roboty. Jejími instancemi jsou vozidla vybavená robotími osádkami – takovou robotí pracovní četou. Jedna továrna může vyrábět třeba vo-zidla s četou hasičů, druhá vozidla s četou opravářů, třetí vozidla s četou malířů.

Každá továrna vyrábí všechna vozidla stejná s naprosto stejnými osádkami. Každý člen osádky vozidla je specializovaný na jedinou funkci, kterou je reakce na konkrétní zprávu a představuje tak ekvivalent metody.

Vozidlo představující instanci je vybaveno radiostanicí, takže může z okolní-ho světa přijímat zprávy. Když přijme zprávu, která vyžaduje splnění některého z úkolů, pro které bylo postaveno, pověří příslušného specializovaného robota (me-todu), aby úkol splnil. Robot může při plnění úkolu posílat zprávy libovolné osádce (včetně té svojí), a požádat ji, aby vykonala nějakou akci potřebnou pro splnění úkolu.

Ikonou, kterou vidíte vlevo, budu v dalším textu uvozovat poznámky, které se budou odvolávat na výše uvedenou analogii. Budete-li si je chtít někdy projít všechny jednu po druhé, podívejte se do rejstříku na heslo analogie – tam najdete seznam stránek, na nichž se vyskytují.

2.3 Třídy a jejich instance

Vytváříme svou první instanci V jazyku Java vytváříme instance tříd zasláním zprávy sestavené z klíčového slo-va new, za nímž uvedeme název třídy, jejíž instanci vytváříme, a dvojici kulatých závorek. Tím zavoláme speciální funkci, které říkáme konstruktor. Konstruktor

Page 44: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

44 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 44 z 433

požadovanou instanci vytvoří a vrátí odkaz, prostřednictvím nějž se budeme na nově vytvořenou instanci obracet.

Vyzkoušíme si vše v praxi. Budeme pokračovat v práci na projektu 02_Objekty, který jsme v minulé kapitole otevřeli. V tomto projektu je 6 tříd:

Plátno – Instance třídy Plátno je jedináček. Představuje plátno, na kterém se budou zobrazovat námi vytvořené tvary.

Obdélník, Trojúhelník a Elipsa – Jejich instance představují objekty, které lze na plátno nakreslit a z nichž můžeme sestavovat složitější tvary.

Barva – Třída má právě devět předem vytvořených instancí představujících základní barvy: černou, modrou, červenou, fialovou, zelenou, azurovou, žlu-tou, bílou a krémovou (další již vytvořit nedovolí). Plátno i zobrazované tva-ry musí být nakresleny jednou z těchto barev.

Každá třída má definovanou svoji oblíbenou (implicitní) barvu. Časem si ukážeme, jak barvu instance ovlivnit.

Směr – Instance třídy Směr představují čtyři hlavní a čtyři vedlejší světové strany, tj. východ, severovýchod, sever, severozápad, západ, jihozápad, jih a jihovýchod. Pomocí těchto instancí je možno definovat směr, do kterého bude vytvářený trojúhelník natočen. Nezadáme-li směr natočení, vytvoří se rovno-ramenný trojúhelník s vrcholem otočeným na sever.

Vyjmenované třídy jsou v diagramu tříd zobrazeny jako vodorovně rozdělené obdélníky, v jejichž horní části je zapsán název třídy. Jazyk UML, od jehož pravidel se způsob kreslení tříd v BlueJ odvozuje, sice definuje i obsah spodní části, ale autoři prostředí BlueJ vás nechtěli hned zpočátku zahrnovat záplavou informací a v zájmu maximální přehlednosti diagramu tříd ponechali spodní část obdélníku prázdnou.

Zkuste nyní vytvořit např. instanci obdélníku. Klepněte pravým tlačítkem na ob-délník představující třídu Obdélník. Rozbalí se místní nabídka, v jejíž horní části je seznam konstruktorů uvozených operátorem new. Klepněte na ten z uvedených konstruktorů, který má za sebou prázdné závorky (viz obr. 2.1).

Page 45: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 45

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 45 z 433

Obrázek 2.1 Vytvoření instance třídy Obdélník

BlueJ otevře dialogové okno (viz obr. 2.2), v němž vás seznámí se základními in-formacemi o vyvolávaném konstruktoru a jím vytvářené instanci a zároveň se vás zeptá, jak budete chtít pojmenovat odkaz na vytvářený objekt, přičemž vám na-bídne jméno odvozené od jména třídy, jejíž instanci vytváříte.

Obrázek 2.2 Dialogové okno po zavolání bezparametrického konstruktoru.

Page 46: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

46 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 46 z 433

Pravidla pro tvorbu identifikátorů v jazyce Java Jména odkazů, tříd a dalších pojmenovaných částí programu se označují termí-nem identifikátory, protože nám tyto části identifikují. Každý programovací ja-zyk definuje sadu pravidel, jimž musí jeho identifikátory vyhovovat. V jazyku Java musí identifikátory vyhovovat následujícím pravidlům:

Smějí obsahovat pouze písmena, číslice a znaky „_“ (podtržítko) a „$“ (dolar). Za písmeno je přitom považován jakýkoliv znak, který sada UNICODE po-važuje za písmeno. Sem patří nejen písmena s diakritikou, ale také např. čín-ské či japonské znaky.

Nesmějí začínat číslicí.

Nesmějí být shodné s žádným klíčovým slovem, tj. s žádným ze slov: abstract boolean break byte case catch class const continue default do double else enum extends final finally float for goto char if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while

Délka identifikátorů (počet jejich znaků) není v jazyku Java omezena – to u řady jiných programovacích jazyků neplatí.

Java patří mezi jazyky, kterým záleží na tom, zda napíšete identifikátor velkými či malými písmeny. Identifikátory něco, Něco a NĚCO jsou proto chápány jako tři různé identifikátory.

Jak jsem již řekl, podle definice jazyka patří mezi písmena všechny zna-ky včetně českých, ruských či japonských, nicméně některá prostředí se s neanglickými znaky nekamarádí. Ověřte si proto tuto skutečnost před tím, než začnete identifikátory s diakritikou používat.

Java sice délku identifikátorů nijak neomezuje, avšak prostor pro zob-razení jména odkazu je omezený. Při zadávání názvů ručně vytváře-ných instancí proto dávejte přednost kratším názvům (tak do 10 znaků), které se zobrazí celé.

Page 47: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 47

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 47 z 433

Vytváříme svou první instanci – pokračování Máme tedy otevřené dialogové okno z obr. 2.2 na straně 45. Protože nemáme žád-ný pádný důvod definovat nějaký zvláštní vlastní název, můžeme akceptovat ná-zev, který nám program nabídne. Po potvrzení jména odkazu vytvoří BlueJ instanci třídy Obdélník a odkaz na ni umístí do zásobníku odkazů, který se nachá-zí pod diagramem tříd.

Program nikdy nemůže pracovat s instancí, ale vždy pouze s odkazemna ni. Použijeme-li naši analogii, pak bychom mohli říci, že nikdy ne-můžete mít v držení celou pracovní četu (= instanci), protože byste ji ani neunesli. Budete mít vždy pouze její telefonní číslo (= odkaz), na které jí můžete zavolat, kdykoliv od ní budete něco potřebovat (budete jí chtít poslat zprávu).

Obrázek 2.3 Odkaz na první instanci v zásobníku odkazů

Položky v zásobníku odkazů jsou zobrazeny jako obdélníky se zaoblenými rohy obsahující dvouřádkový popisek: v prvním řádku je uvedeno jméno odkazu a v druhém řádku jméno třídy, na jejíž instanci daná položka odkazuje.

Na počátku kapitoly jsme si řekli, že se při vytváření instance zavolá speciál-ní funkce nazývaná konstruktor. Konstruktor instancí třídy Obdélník je napro-

Page 48: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

48 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 48 z 433

gramován tak, že při vytváření instance zároveň zařídí, aby se na plátně zobrazil příslušný obrázek a pokud plátno ještě neexistuje, tak aby se vytvořilo (tj. požádá třídu Plátno, aby vytvořila svoji instanci), protože jinak by nebylo na co kreslit. Vedlejším efektem vytvoření první instance tedy je, že se otevře okno plátna a na něm bude zobrazen náš obdélník (viz obr. 2.4).

Obrázek 2.4 Okno plátna s nakresleným obdélníkem

Pokud okno plátna nevidíte, bude asi schované pod nějakým jiným ok-nem – dost možná právě pod oknem BlueJ. Zobrazíte je tak, že na pane-lu úloh klepnete na jeho tlačítko (naše plátno se chová jako samostatná aplikace, tak má na panelu úloh svoje tlačítko), nebo tak, že budete po-stupně minimalizovat či posouvat otevřená okna, až okno plátna najde-te.

Třída Plátno je zrovna třídou, která vám nedovolí vytvořit její instanci prostřednictvím konstruktoru (tj. pomocí operátoru new), protože trvá na tom, aby její instance byla jedináček (chce, aby se všechny obrazce kreslili na stejné plátno). To ale naštěstí nevadí, protože se s ní třídy Obdélník, Elipsa a Trojúhelník (přesněji jejich konstruktory) dokáží do-hodnout, aby vše fungovalo tak, jak to fungovat má. Zanedlouho si ukážeme náhradní způsob, jak získat odkaz na instanci plátna.

Page 49: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 49

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 49 z 433

Posíláme instanci zprávu Nyní zkusíme nově vytvořenému obdélníku poslat nějakou zprávu. Klepněte pro-to pravým tlačítkem myši na odkaz na náš nově vytvořený obdélník (tj. na zaob-lený obdélník v zásobníku odkazů). Rozbalí se poměrně dlouhá místní nabídka, která má v horní části černé položky zastupující zprávy (vysvětlení proč vypadají právě takto si necháme na později) a v dolní části červené položky zastupující pří-kazy pro vývojové prostředí. Najeďte myší do horní části a zadejte příkaz void posunVpravo() (viz obr. 2.5) – a ejhle, obdélník se po plátně opravdu posune vpra-vo.

Obrázek 2.5 Poslání zprávy odkazované instanci

Pošlete instanci další zprávy. Prozatím se však omezte na zprávy void posunVpravo(), void posunVlevo(), void posunVzhůru() a void posunDolů(). (O tom, proč tyto zprávy začínají právě slovem void, si povíme za chvíli.)

Page 50: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

50 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 50 z 433

Vytváříme další instance Říkali jsme si, že běžná třída může mít více instancí. Tak si to hned vyzkoušíme a vytvoříme další obdélník – požádejte třídu Obdélník stejným způsobem o vytvo-ření další instance. Potvrďte opět nabízený název a zkontrolujte, že se v zásobníku odkazů objevil příslušný odkaz a že se v levém horním rohu plátna opravdu na-kreslil druhý obdélník.

Nyní si již můžete vybírat mezi dvěma adresáty svých zpráv. Z čí místní na-bídky zadáte příkaz k poslání zprávy, té instanci se zpráva odešle a ta instance na ni zareaguje.

Vytvořte instance dalších tříd. Pošlete jednotlivým instancím zprávy void posunVpravo(), void posunVlevo(), void posunVzhůru() a void posunDolů() a ověřte, že na každou zprávu reaguje ten objekt, kterému jste ji poslali.

Posunu obrazců se dosahuje obdobným způsobem, jakým byste postu-povali vy, kdybyste je kreslili pastelkami na papír. Máte-li některý z ob-razců posunout do nové pozice, musíte jej nejprve vygumovat ve staré pozici a pak nakreslit v pozici nové.

Budou-li se však dva obrazce překrývat, odmaže se při mazání pře-souvaného obrazce i část obrazce, se kterým se překrývá. Protože o so-bě obrazce nevědí, nemohou tuto nepříjemnost samy napravit.

Odmazanou část obrazce obnovíte tak, že obrazec požádáte, aby se překreslil, tj. pošlete mu zprávu void nakresli().

Tuto nepříjemnou vlastnost odstraníme až ve třetí kapitole, kdy se seznámíme s nástroji, s jejichž pomocí bude možné problém vyřešit.

Když budete mít vytvořených instancí více, už se vám do oblasti vyhrazené pro zásobník instancí nevejdou. Zásobník se proto doplní po stranách o dvě posunové šipky (viz obr. 2.6). Klepnutím na ně obsah zásobníku posunete. Tak budete moci zásobníkem procházet, i když v něm bude větší počet instancí.

Rušení instancí a správa paměti Pokud již nějaký odkaz v zásobníku odkazů nepotřebujete, můžete jej odstranit. K tomu je třeba zadat v jeho místní nabídce povel Odstranit (naleznete jej ve spodní části nabídky – viz obr. 2.6).

Page 51: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 51

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 51 z 433

Obrázek 2.6 Zrušení odkazu na objekt

Po zadání povelu odkaz ze zásobníku zmizí. To však ještě neznamená, že byl ob-jekt doopravdy zrušen. Jak jsem již řekl, v zásobníku nejsou skutečné objekty, ale pouze odkazy na ně. O tom, jestli a kdy má být příslušný objekt doopravdy zru-šen, rozhoduje správce paměti, což je speciální program, který má na starosti při-dělování paměti nově vznikajícím objektům a rušení objektů, které již nikdo nepotřebuje.

Správce paměti je v anglických textech někdy označován jako garbage collector, což v překladu znamená sběrač odpadků neboli popelář. Pro-gramátoři si na něm totiž nejvíce cení, že po nich dokáže uklidit. Někte-ří autoři tento termín nepřekládají, jiní používají termín sběrač neplatných objektů. Jak jsem již ale řekl, tento program nemá na starosti pouze úklid paměti, ale také její přidělování, a proto dávám přednost termínu správce paměti.

Page 52: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

52 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 52 z 433

Odstranění odkazu ze zásobníku odkazů nebude mít žádný vliv na po-dobu plátna. Obrazce na plátně nemůžeme s objekty ztotožnit, jsou totiž pouze výsledkem nějaké činnosti těchto objektů. Budete-li chtít ob-jekt z plátna smazat, pošlete mu zprávu void smaž(). Tuto zprávu mu však musíte poslat před tím, než jej ze zásobníku odstraníte, protože pak již nebudete mít k dispozici odkaz, takže nebudete mít komu zprá-vu poslat.

„Ukliďte“ zásobník odkazů – odstraňte z něj odkazy na všechny dopo-sud vytvořené objekty, ale před tím je nejprve smažte.

2.4 Zprávy žádající o hodnotu Zprávy, které jsme doposud vytvořeným instancím posílali, požadovaly po in-stancích nějakou (viditelnou) akci. V programech však často nepotřebujeme, aby instance něco provedla, ale aby nám něco prozradila (většinou o sobě).

Abychom mohli s touto informací pracovat, předáváme při poslání zprávy žádající informaci také požadavek, jakého typu má daná informace být (např. čís-lo, text, apod.).

V místních nabídkách instancí a tříd začínají příkazy, jejichž zadáním vysílá-me zprávy, názvem datového typu, který má mít vrácená informace. Příkazy vysí-lající zprávy, které po instanci žádnou informaci nepožadují a spokojí se s tím, že oslovená instance provede nějakou akci, začínají magickým slůvkem void (anglic-ky prázdno). Příkazy vysílající zprávy, které po instanci chtějí, aby nám vrátila ně-jakou informaci, začínají názvem typu, který bude mít vracená hodnota.

Datové typy Než se rozhovoříme o tom, jak pracovat se zprávami, které vracejí hodnoty, mu-síme si nejprve povědět něco o datových typech. Typ údaje popisuje, co je daný údaj zač. Svůj typ mají veškerá data, se kterými program pracuje. Java (a naprostá většina ostatních moderních jazyků) trvá na tom, aby byl u každého údaje předem znám jeho typ. Za to se nám odmění tím, že bude pracovat mnohem rychleji (ne-musí v průběhu výpočtu bádat nad tím, co je které „dato“ zač) a navíc odhalí řadu našich chyb již v zárodku.

Page 53: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 53

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 53 z 433

Java rozděluje datové typy na primitivní a objektové. Primitivní datové typy, mezi něž patří např. čísla, jsou zabudovány hluboko v jazyku a jejich chování je pevně dané. K vytvoření hodnot primitivních datových typů nepotřebujeme kon-struktor, ale na druhou stranu jim zase nemůžeme posílat žádné zprávy.

Objektové datové typy jsou nám již dobře známé třídy. Těch bývá v programu definováno většinou mnohem víc. V našem úvodním pidiprojektu je jich definováno 6, v rozsáhlejších projektech jich mohou být definovány stovky a tisíce. Jenom ve standardní knihovně jich je definováno přes 4000 (primitivních datových typů je 8).

Objektové datové typy (tj. třídy) si může každý programátor nadefinovat sám. S jistou rezervou bychom mohli říci, že objektové programování spočívá v návrhu a implementaci těch správných objektových datových typů.

V nejbližších kapitolách se budeme setkávat s následujícími datovými typy:

Primitivní datové typy int označuje typ celých čísel, jejichž hodnoty se mohou pohybovat přibližně v rozsahu ± 2 miliardy (přesně od -2 147 483 648 do +2 147 483 647). Název typu je zkratkou ze slova integer (= celé číslo). Je to nejpoužívanější datový typ.

boolean definuje typ logických hodnot, které mohou nabývat pouze hodnot true (pravda, ano, …) a false (nepravda, ne, …). Název tohoto typu nám připomíná matematika George Boola, který se v 19. stoletý zabýval logikou a na jehož počest se práce s logickými výrazy označuje jako Booleova algebra.

double označuje typ reálných čísel. Čísla typu double se v Javě pamatují s přesností na 15 platných číslic a v rozsahu do 10308 (číslo s 308 nulami). Své jméno dostal od toho, že v době svého zavedení (šedesátá léta minulého sto-letí) definoval čísla s dvojitou přesností oproti číslům tehdy většinou použí-vaným.

Nepleťte si platné číslice a desetinná místa. Např. číslo 0,00123 má pětdesetinných míst, ale pouze tři platné číslice. Na druhou stranu číslo12300 nemá žádné desetinné místo a může mít tři až pět platných číslicpodle toho, jsou-li závěrečné nuly přesné, anebo vznikly zaokrouhle-ním.

Page 54: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

54 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 54 z 433

Objektové datové typy String definuje typ znakových řetězců, tj. posloupností znaků, které chápeme jako jediný objekt. Aby program zadávané řetězce správně rozpoznal, musíte tyto texty zadávat vždy uzavřené mezi uvozovkami – např. "Znakový Řetězec".

String je druhý nejpoužívanější datový typ. Své jméno dostal podle ang-lického sova string = řada, série, řetěz(ec), provázek, …

Mezi objektovými typy má typ String výjimečné postavení, protože je „zapuštěn“ hluboko do definice jazyka a tvůrci jazyka mu oproti ostatním ob-jektovým typům přidali některé dodatečné vlastnosti, které u jiných datových typů nenajdeme. S řadou z nich se seznámíte již v této a příští kapitole.

Barva, Elipsa, Obdélník, Plátno, Směr, Trojúhelník jsou objektovými typy, s nimiž jste se již seznámili v diagramu tříd projektu. Na rozdíl od typu String, který je součástí standardní knihovny, jsem pro vás tyto datové typy napro-gramoval já a vy od příští kapitoly začnete programovat vlastní.

Těžiště objektového programování spočívá v umění definovat správné objektové datové typy a v umění správně definovat jejich vzájemnou spolu-práci.

Před chvílí jsem vám říkal, že objektových datových typů je ve standardní knihovně přes 4000 a prozatím jsme si pověděli pouze o jednom. S dalšími objek-tovými datovými typy ze standardní knihovny se začneme seznamovat až v dru-hé části této učebnice, kdy již budou vaše znalosti dostatečné k tomu, abyste schopnosti těchto typů dokázali náležitě využít.

Vracení hodnot primitivních typů Tak dost již teoretických řečí a pojďme spolu zase něco dělat. Budeme-li chtít po nějakém objektu, aby nám prozradil svoje souřadnice na plátně, pošleme mu po-stupně zprávy int getX() a int getY(), po nichž nám objekt vrátí vždy celé číslo (int) udávající jeho vzdálenost v bodech od levého horního rohu plátna. Bude-me-li naopak chtít, aby nám prozradil svůj rozměr, pošleme mu postupně zprávy int getŠířka() a int getVýška(), po nichž nám objekt vrátí vždy celé číslo udáva-jící příslušný rozměr v bodech. Informaci, kterou požadujeme, nám objekt vrátí v textovém poli dialogového okna, které se v reakci na vyslání zprávy otevře.

Page 55: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 55

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 55 z 433

Tady se možná někteří z vás podivili, proč je součástí názvů uvedených zpráv slovíčko get. Je to všeobecná konvence, podle které názvy zpráv, na které příslušná instance odpovídá vrácením nějaké hodnoty popisu-jící její stav, začínají předponou get za níž následuje název oné vracené informace. Je-li vracená hodnota logickou hodnotou, používá se místopředpony get předpona is.

Na konci minulé kapitoly jsme zásobník i plátno vyčistili, takže můžeme začít znovu – vytvořte nový obdélník a nazvěte jej o1. Tomuto obdélníku pak zašlete zprávu int getŠířka(). Jako odpověď otevře BlueJ dialogové okno, v němž nám požadovaný údaj prozradí (viz obr. 2.7).

Obrázek 2.7 Vrácená hodnota po zaslání zprávy int getŠířka() objektu o1

Hodnota, kterou instance vrací, bývá označována tzv. návratová hod-nota. Někdy se používá také termín výstupní hodnota.

Vracení hodnot objektových typů Výše popsané zprávy vracely celočíselnou hodnotu, tj. hodnotu primitivního ty-pu. Nic však nebrání tomu, aby zprávy požádaly o hodnoty objektových typů, přesněji odkazy na tyto hodnoty. (Jak jsme si již několikrát řekli, program nikdy nepracuje s instancemi, ale vždy pouze s odkazy na ně.)

Práce s takovýmito návratovými hodnotami je však trochu komplikovanější, protože k převzetí odkazu musíme ještě zadat název položky, která bude vytvo-řena v zásobníku odkazů a do které bude odkaz na požadovaný objekt uložen.

Zprávou požadující vrácení hodnoty objektového typu je např. zpráva getBarva(), kterou mají všechny tři geometrické tvary na počátku svého seznamu. Podívejme se nyní, jak budeme postupovat při poslání této zprávy a zejména pak zpracování obdrženého výsledku.

Page 56: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

56 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 56 z 433

V zásobníku odkazů máme odkaz o1 ukazující na obdélník, jehož barvu chceme zjistit. Pošleme proto tomuto obdélníku zprávu getBarva(). Obdélník zprávu zpracuje a vrátí odkaz na svoji barvu. BlueJ pak otevře dialogové okno, v němž nás šipkou v textovém poli upozorní na to, že vrácenou hodnotou byl odkaz na objekt. Oproti minulé verzi dialogového okna ožijí v tomto okně dvě tlačítka po pravé straně:

Tlačítko Prohlížet nám umožní se podívat do útrob vraceného objektu. Proza-tím si jej nebudeme všímat – budeme si o něm povídat v kapitole Výlet do nit-ra instancí na straně 68.

Tlačítko Získat odkaz je určeno pro převzetí vraceného odkazu a jeho uložení do zásobníku odkazů. S tím si budeme „hrát“ nyní.

Obrázek 2.8 Předání návratové hodnoty objektového typu

Chtěli jsme odkaz, tak stiskneme tlačítko Získat odkaz. BlueJ otevře dialogové okno, v němž se nás zeptá na požadované jméno (identifikátor) nově vzniklého odkazu. Protože víme, jaká je barva obdélníku, můžeme odkaz nazvat přímo názvem této barvy a stiskem OK svoji volbu potvrdit.

Obrázek 2.9 Zadání názvu vráceného odkazu

BlueJ pak vzápětí uloží odkaz do zásobníku, kde s ním můžeme pracovat podobně jako s odkazy na objekty, o jejichž vytvoření jsme požádali konstruktor.

Page 57: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 57

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 57 z 433

Použijeme-li naši analogii (viz podkapitola Analogie na straně 43), pak bychom mohli říci, že pošleme-li někomu zprávu, v níž žádáme hodno-tu objektového typu, dostaneme vždy pouze telefonní číslo na přísluš-nou instanci.

Obrázek 2.10 S odkazem získaným zasláním zprávy můžeme pracovat jako s jakýmkoliv jiným

Vzácnou výjimkou mezi objektovými typy je typ String. Když jsem se o něm po-prvé zmiňoval, říkal jsem, že se v některých situacích chová obdobně jako primi-tivní datové typy. Jednou z takovýchto situací je v programu BlueJ předávání návratové hodnoty, při němž se String chová současně jako primitivní typ, proto-že zobrazí požadovanou hodnotu, a současně jako objektový datový typ, protože nám umožní získat odkaz.

Zkuste např. poslat odkazu na červenou barvu zprávu String getNázev(). V dialogovém okně, které pak BlueJ otevře, uvidíte jak hodnotu tohoto řetězce, tak živá tlačítka nabízející možnost prohlížení a získání odkazu.

Page 58: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

58 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 58 z 433

Obrázek 2.11 Při vracení objektu typu String se zobrazí i příslušný řetězec

2.5 Parametry a jejich typy Prozatím jsme se omezovali na používání konstruktorů, jejichž závorky za ná-zvem konstruktoru byly prázdné. Tyto tzv. bezparametrické konstruktory vytvá-řely obrazce ve stále stejném místě plátna, stále stejně veliké a pokaždé stejně vybarvené.

Nyní si ukážeme, jak tento nudný stereotyp změnit. Budeme modifikovat chování konstruktorů prostřednictvím parametrů, s jejichž pomocí budeme moci všechny výše uvedené charakteristiky nastavit. Prostřednictvím parametrů pře-dáme konstruktoru hodnoty, kterými budeme specifikovat některé naše požadav-ky na vytvářené instance.

Někteří autoři používají pro parametry termín argument, občas se také můžete setkat s termínem vstupní hodnota jako opozitum k výstupní hodnotě vracené po zaslání některých zpráv.

Jak jste si mohli všimnout v místních nabídkách, jednotlivé konstruktory se liší tím, kolik parametrů mají v závorkách uvedeno a které to jsou. Při definici kon-struktoru proto tvůrce uvede v závorkách za názvem třídy čárkami oddělený se-znam parametrů, které je konstruktor schopen zpracovat.

Definice několika verzí konstruktorů s různými sadami typů parametrůbývá označována jako přetěžování daného konstruktoru. Jednotlivé verze se pak nazývají přetížené.

Aby program dokázal s předanými hodnotami co nejefektivněji pracovat, musí vědět, co jsou zač. Je např. zřejmé, že např. konstruktor trojúhelníků bude zcela jinak pracovat s barvou svého objektu, jinak se směrem, do nějž má být vytvářený

Page 59: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 59

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 59 z 433

trojúhelník natočen a zcela jinak s číslem představujícím jeho rozměr nebo sou-řadnici.

Seznam parametrů v závorkách proto obsahuje nejenom názvy (identifikáto-ry) jednotlivých parametrů, které nám napoví jejich význam, ale také jejich datové typy, podle nichž počítač pozná, jak má s daným parametrem pracovat.

Vyvolání konstruktoru s parametry Ukažme si vše na příkladu. Zkusíme vytvořit další instanci třídy Obdélník, u které si objednáme její umístění i velikost. Pošleme proto zprávu, jež spustí konstruktor, který nám umožní zadat jak x-ovou a y-ovou souřadnici vytvářeného obdélníku, tak jeho rozměry. Vhodná zpráva je v nabídce uvedena jako příkaz (viz obr. 2.12)

new Obdélník( x, y, šířka, výška )

Obrázek 2.12 Zavolání konstruktoru umožňujícího zadat požadovanou souřadnici a rozměr

Po zadání příslušného povelu se otevře velké dialogové okno (viz obr. 2.13), v němž nás BlueJ vyzve nejenom k zadání (nebo alespoň potvrzení) názvu odkazu na vytvářený objekt, ale také k zadání hodnot jednotlivých parametrů.

Page 60: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

60 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 60 z 433

Obrázek 2.13 Okno konstruktoru s parametry

Horní část okna zobrazuje popis funkce volaného konstruktoru spolu s popisem významu jednotlivých parametrů.

Neděste se toho, že popis je trochu kryptický (zašifrovaný) – je to opsa-ná část programu sloužící k automatizované tvorbě dokumentace. Aby Java mohla tuto dokumentaci vytvořit, požaduje dodržení jistých kon-vencí, které sice trochu ztěžují přímé čtení programu, ale na druhou stranu umožňují právě vytvoření profesionální dokumentace.

Tvorbě dokumentace bude věnována pasáž Automaticky generovanádokumentace na straně 160. Prozatím si pamatujte, že za každým @paramnásleduje název parametru a jeho popis.

V této části nemusí být vždy uvedeny všechny parametry a dokonce v ní může chybět i popis. Bude zde vždy jen to, co se tvůrce programu rozhodl popsat. Berte proto tento návod pouze jako pomůcku a smiřte se s tím, že programy tvůrců, kteří jsou líní dělat dokumentaci, tako-vouto nápovědu nabízet nebudou. Snažte se k nim nezařazovat a své programy vždy řádně dokumentujte.

Page 61: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 61

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 61 z 433

Zde bych vás chtěl upozornit na poslední řádek horní části, který je těsně nad dě-lící čarou a je vysazen tučně. Zde je zobrazena hlavička konstruktoru, která speci-fikuje to nejdůležitější, co potřebujete o konstruktoru vědět.

V hlavičce je uveden název konstruktoru (a tím i název třídy, jejíž instanci vy-tváříme) následovaný závorkami se seznamem všech parametrů (na rozdíl od komentáře, kde může něco chybět, tady nic chybět nesmí). Je-li parametrů více, jsou jejich deklarace odděleny čárkami. Všimněte si, že u každého parametru je uveden nejprve typ a za typem název (identifikátor) daného parametru. Takto budete od příští kapitoly uvádět parametry i vy ve svých programech.

Autoři BlueJ se snažili ulehčit orientaci v situacích, kdy je zadávaných para-metrů více a zopakovali typ i název parametru vpravo vedle vstupního pole, do nějž se má zadat jeho hodnota.

Jednotlivé prvky okna jsme si probrali a můžeme začít vyplňovat. Pro nově konstruovaný obdélník zadáme název o2. Dejme tomu, že vytvoříme obdélník tak, aby zabral celé plátno, které má v počátečním stavu rozměr 300×300 bodů. Zadáme proto parametry podle obrázku 2.14.

Obrázek 2.14 Vyplněné okno parametrického konstruktoru

Po zadání příkazu se na plátně objeví vytvořený obdélník. Protože zabírá celé plátno a protože je červený, přebarví celé plátno na červeno.

Page 62: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

62 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 62 z 433

1. Vytvořte další obrazce, kterým zadáte požadované souřadnice a rozměry.

2. Uveďte plátno do (zdánlivě) počátečního stavu tím, že obdélníku zabírajícímu celé plátno pošlete zprávu void smaž(). Po této zprávě obdélník překreslí celou svoji plochu barvou pozadí plátna, čímž automaticky všechny ostatní zobrazené tvary smaže.

Parametry objektových typů Z konstruktorů, které každá ze tříd Obdélník, Elipsa a Trojúhelník nabízí, jsme doposud použili pouze dva: bezparametrický konstruktor a konstruktor, který umožnil zadat počáteční pozici a rozměr obdélníku. Při volání zbylých konstruk-torů je totiž třeba zadat parametr objektového typu – a to se nyní naučíme.

Prozatím jsem vám všechno předváděl na obdélnících. Aby to ostatním ob-razcům nebylo líto, budeme si chvíli hrát s nimi.

Každý z tvarů má definovanou svoji implicitní barvu, kterou se na plátno na-kreslí. Budeme-li chtít vytvořit obrazec jiné barvy, musíme zavolat konstruktor, který je schopen převzít požadovanou barvu jako parametr, a vytvářený obrazec s ní vybarvit. Hodnotu objektového typu můžeme zadat dvěma způsoby:

Máme-li v zásobníku odkaz na příslušnou hodnotu, stačí klepnout myší do vstupního textového pole příslušného parametru a potom klepnout v zásob-níku na tento odkaz. BlueJ sám zapíše potřebný text.

Nemáme-li v zásobníku odkaz na předávanou hodnotu, musíme ji zapsat tex-tově. Tento způsob zadávání se ale budeme učit až za chvíli.

V zásobníku odkazů máme odkaz na červenou barvu. Rozhodneme-li se proto vy-tvořit ve středu plátna červený kruh o průměru 100 bodů, můžeme pro zadání barvy tento odkaz využít – viz obr. 2.15.

Page 63: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 63

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 63 z 433

Obrázek 2.15 Objektový parametr můžeme zadat klepnutím na odkaz v zásobníku

Posílání zpráv s parametry Ukážeme si nyní, že parametry můžeme využívat i při předávání zpráv. Kon-struktory jsou pouze zvláštním druhem zpráv, takže většinu toho, co můžeme použít u konstruktorů, můžeme použít i u obyčejných zpráv.

Přesouvání jsme si již užili dost, tak zkusíme něco jiného – např. změnit veli-kost objektu. Zkuste změnit rozměry „celoplátnového“ obdélníka o1 tak, aby zů-stal tak široký jako plátno (tj. 300 bodů), ale aby byl pouze 20 bodů vysoký. Zadejte proto v jeho místní nabídce povel void setRozměr(šířka, výška) a v ná-sledně otevřeném dialogovém okně zadejte příslušné hodnoty jeho parametrů.

Jak jste si jistě domysleli, stejně jako konvence s předponou get existuje i konvence s předponou set, kterou začínají názvy zpráv, které nastavu-jí hodnoty ovlivňující stav instance.

Page 64: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

64 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 64 z 433

Protože upravovaný obdélník zabíral celé plátno, zmizely se změnou jeho rozměrů i všechny další tvary, které jste na plátno při řešení po-sledního úkolu nechali nakreslit. V poznámce na str. 50 jsem říkal, že obrazec znovu zobrazíte tím, že mu pošlete zprávu void nakresli(). To však samozřejmě není jediné řešení. Obrazec se překreslí také po zaslá-ní kterékoliv ze zpráv, které mění jeho pozici nebo polohu.

Pošlete i ostatním obrazcům v zásobníku zprávu, v níž je žádáte o změ-nu jejich pozice nebo rozměru. Pokud vás ještě neomrzelo posouvání objektů, můžete jim zkusit poslat i zprávy void posunVpravo(vzdále-nost) nebo void posunDolů(vzdálenost), které posunou příslušný objekt o zadaný počet bodů požadovaným směrem.

Stejně jako konstruktorům můžete parametry objektových typů předávat i zprá-vám. Každému z tvarů můžete např. poslat zprávu void setBarva(nová), v níž mu oznámíte, na jakou barvu se má přebarvit.

Vytvořte pomocí bezparametrického konstruktoru instance elipsy a trojúhelníku a získejte instance jejich barev. Nastavujte pak různým ob-razcům různé barvy. Zkuste pomocí příkazů vytvořit nějaký zajímavý obrázek.

2.6 Metody třídy Někdy potřebujeme zaslat zprávu, která se netýká jedné konkrétní instance, ale týká se celé třídy. Rodičovská třída totiž své instance po jejich zřízení neopouští, ale uchovává informace, které jsou pro všechny její instance společné, aby je in-stance mohly v případě potřeby využít. Zároveň také často poskytuje metody, které umožňují tyto informace ovlivnit nebo vykonat jinou akci, jež není svázána s žádnou její konkrétní instancí.

Page 65: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 65

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 65 z 433

V naší analogii bychom mohli metody třídy definovat jako roboty, kteří pracují přímo v továrně. Stejně jako pracovní četě (=instanci) je i v to-várně (=třídě) vyhrazen pro každou zprávu, kterou lze do továrny po-slat, speciální robot (= metoda třídy), který zabezpečí správnou reakcina tuto zprávu.

Podívejme se např. na náš předchozí příklad s geometrickými tvary. Na příkaz void přesunXXX() se obrazce přesunou o předem stanovaný počet obrazových bo-dů směrem XXX. Velikost tohoto kroku je přitom pro všechny instance dané třídy stejná – je to právě ten druh informace, kterou instance sdílejí prostřednictvím své rodičovské třídy. Rozhodneme-li se proto velikost tohoto implicitního přesunu změnit, musíme poslat zprávu celé třídě.

Otevřete si znovu místní nabídku třídy Obdélník (viz např. obrázek 2.1 na straně 45). V její střední části si můžete všimnout dvou položek, jejichž zadáním posíláme zprávu celé třídě. Zasláním zprávy int getKrok() se třídy zeptáte na současnou velikost kroku, zasláním zprávy void setKrok(velikost) nastavíte no-vou velikost tohoto kroku. Tato velikost pak bude platit pro všechny instance da-né třídy, tj. i pro ty, které vznikly před tím, než jste nastavili novou velikost kroku a které proto doposud používaly velikost původní.

Otevřete místní nabídku některé třídy a zadejte v ní příkaz void setKrok(velikost). V následně otevřeném dialogovém okně zadejte no-vou velikost kroku – dejme tomu 100 bodů – a vyzkoušejte, jak budou jednotlivé instance reagovat. Všimněte si, že se krok změnil pro všech-ny (staré i nové) instance dané třídy, avšak instance jiných tříd na něj vůbec nereagují a dál se přesouvají původně nastavenou velikostí kro-ku.

Zkuste znovu změnit velikost kroku, avšak tentokrát pro jinou třídu. Nastavte jí krok např. na 5 bodů a znovu vše vyzkoušejte.

Velmi užitečnou metodou třídy bude pro vás při vašich experimentech metoda smaž() třídy Plátno. Po našich předchozích pokusech máte již plátno jistě zaplněné několika grafickými tvary. Zavolejte proto tuto metodu z místní nabídky třídy plátno a budete mít plátno čisté.

Page 66: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

66 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 66 z 433

Metoda smaž()pouze smaže plátno, tj. „natře“ je na aktuální barvu po-zadí. V žádném případě neodstraňuje objekty z paměti – pouze smaže obrázek, který na plátno nakreslily. Můžete se o tom přesvědčit tak, že kterýkoliv z objektů, na nějž máte odkaz, požádáte, aby se znovu na-kreslil, tj. zadáte v jeho místní nabídce příkaz void nakresli().

Metoda smaž() třídy Plátno není pravou metodou třídy. Měla by to být správně metoda konkrétního plátna. Protože však víme, že toto plátno je jedináček, je z jistého úhlu pohledu jedno, jak metodu deklaruji. V našem programu je definována jako metoda třídy proto, abyste k ní mě-li snadný přístup a nemuseli kvůli smazání plátna pracně získávat od-kaz na jeho instanci. Přiznávám, že z hlediska správného programování je to nečisté, ale pro naše praktické účely je to výhodné.

Třídy, které chtějí, aby jejich instance byly jedináčci, musí spolupracujícím třídám a jejich instancím nějak umožnit získat odkaz na tohoto jedináčka, aby s ním moh-ly komunikovat. Používají k tomu většinou metodu, která se tváří, že pro vás po-žadovaný odkaz vyrobí. Na rozdíl od konstruktorů však nezaručuje, že po každém zavolání vrátí odkaz na nově vytvořenou instanci. Naopak – klidně vám pokaždé vrátí to samé.

Takovou metodu má i třída Plátno – vyvoláte ji zasláním zprávy Plátno getPlátno() a získáte od ní odkaz na třídního jedináčka.

Získejte odkaz na instanci plátna a vyzkoušejte jeho metody. Nelekněte se však, když plátno po změně svého rozměru nebo barvy pozadí všechny objekty smaže. Jak jste si již mohli vyzkoušet, po zprávě void nakresli() se daný tvar znovu překreslí.

2.7 Instance versus odkaz V jazyku Java program nikdy neobdrží instanci, kterou vytvořil. Obdrží pouze odkaz na tuto instanci. Vlastní instance je zřízena někde ve zvláštní paměti, které se říká halda (anglicky heap). Ta je vyhrazena právě pro zřizování objektů a má ji na starosti správce paměti (garbage collector – doslova popelář). Ten se stará mi-mo jiné o to, aby instance, které již nikdo nepotřebuje, nezabíraly v paměti zby-tečně místo. Aby mohl tuto správu provádět dostatečně efektivně, nikoho k haldě

Page 67: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 67

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 67 z 433

nepustí a program se proto musí k objektům na haldě obracet prostřednictvím příslušných odkazů.

V klasických programovacích jazycích se o přidělování a uvolňování paměti staral programátor. Ale právě správa paměti byla zdrojem veli-ce záludných chyb. Proto moderní jazyky svěřují správu paměti speci-álnímu programu, který svoji práci zastane v převážné většině případů mnohem lépe než člověk. Produktivita programátorské práce tímto ře-šením stoupla několikanásobně.

Jak jsem již řekl na počátku našich hrátek s geometrickými tvary, třída Plátno trvá na tom, že její instance bude jedináček. (Proto nám také nenabízí konstruktor.) Z toho tedy zákonitě vyplývá, že každé zaslání zprávy getPlátno() vrátí odkaz na stejnou instanci. Vyzkoušejte to.

Pošlete třídě Plátno znovu zprávu getPlátno() a opět zadejte v dialogovém okně, že chcete získat odkaz. V zásobníku odkazů se tak objeví další odkaz, pro nějž nám BlueJ nabídne název plátno2. Máme tedy dva odkazy, o nichž tvrdíme, že odkazují na stejný objekt. Přesvědčme se o tom.

Požádejte jeden odkaz, aby jím odkazovaná instance změnila svůj rozměr a pak o totéž požádejte druhý odkaz (zadáte samozřejmě jiný rozměr). Obě dvě žá-dosti ovlivní velikost jednoho a toho samého okna. Oba dva odkazy totiž ukazují na jednu a tu samou instanci.

Nesměšujte nikdy instanci s odkazem na ni! Až budete s objekty pra-covat ve svých programech, pamatujte, že budete mít vždy k dispozici pouze odkazy na objekty, s nimiž pracujete. Může se proto lehko stát, že si budete na několika místech pamatovat odkaz na stejný objekt, ale stejně dobře se může stát, že jediný dostupný odkaz na důležitý objekt omylem přemažete a objekt pak bude sice dál existovat (a třeba dělat nějakou neplechu), ale vy se k němu již nikdy nedostanete.

Jak jsme si již říkali, v naší analogii bychom mohli odkaz chápat jako te-lefonní číslo na danou instanci. K vlastní instanci se nikdy nedostanete a přemažete-li či zahodíte toto telefonní číslo, ztratíte tak také jakouko-liv možnost instanci o něco požádat.

Page 68: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

68 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 68 z 433

2.8 Výlet do nitra instancí Zatím jsme si ukazovali, jak instance reagují na zprávy, které jim posíláme. Nic jsme si ale doposud neříkali o tom, jak instance pozná, kde se má nakreslit, odkud se má přesunout nebo jak zjistí, jakou má její obrazec barvu.

Aby mohl objekt na naše zprávy správně reagovat, musí uchovávat informa-ce popisující stav objektu. Tyto informace označujeme jako atributy.

Své atributy mají jednotlivé instance, ale může je mít i celá třída. Rozlišujeme proto atributy instancí a atributy třídy. Atributy třídy všechny její instance sdílejí a jakákoliv jejich změna se ihned promítne do všech instancí dané třídy.

Někdy se v literatuře setkáte také s termíny vnitřní proměnné nebo členské proměnné. Já však o nich budu v dalším textu hovořit téměř výhradně jako o atributech.

Kdybychom se vrátili k naší analogii, mohli bychom si atributy před-stavit jako zásuvky s informacemi. Atributy instancí má každá instance(četa) ve svém vozidle, atributy třídy jsou v informačním centru v to-várně (=třídě).

Změní-li některý z robotů (=metod) hodnotu uloženou v šuplíku vautě (tj. hodnotu atributu instance), ovlivní tato změna chování ostat-ních robotů (=metod), protože až se půjdou do šuplíku podívat, najdoutam hodnotu, kterou tam jejich kolega uložil.

Změní-li kdokoliv hodnotu uloženou v šuplíku v továrně (tj. hodno-tu atributu třídy), ovlivní tato změna chování všech, kdo se po tétozměně půjdou do tohoto šuplíku podívat, tj. i chování ostatních instancía jejich metod.

Atributy instancí Prozatím jsme o atributech hovořili pouze jako o něčem, co existuje, ale neměli jsme možnost se o tom přesvědčit. Hned to napravíme.

Page 69: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 69

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 69 z 433

Protože nevím, jak moc jste experimentovali, nemohu zaručit, že se bu-dou následující obrázky v knize shodovat s těmi, které vám bude uka-zovat váš počítač. Chcete-li je synchronizovat, zavřete BlueJ a znovu jej otevřete (já to nyní udělám také). Tím počítač inicializujete a zařídíte, žeby naše nejbližší obrázky měly zase vypadat stejně.

Vytvořte bezparametrickým konstruktorem instanci obdélníku a nazvěte ji o1. Vyvolejte pravým tlačítkem myši její místní nabídku a zadejte v ní povel Prohlížet. Tím spustíte prohlížeč objektů (uživatelé Windows si mohou všim-nout, že se na liště úloh objeví další aplikace), který otevře své okno a vypíše v něm hodnoty atributů příslušné instance (viz obr. 2.16).

Obrázek 2.16 Okno prohlížeče atributů instance

Prohlížeč v okně zobrazí jednotlivé atributy dané instance. U každého z nich uve-de jeho typ a název. U atributů primitivních typů a typu String uvede navíc i je-jich hodnotu. U atributů ostatních objektových typů uvede místo hodnoty pouze symbol označující, že se jedná o odkaz. Klepnete-li však na atribut objektového typu, ožije vpravo tlačítka Prohlížet, jehož stiskem můžete otevřít pro odkazova-nou instanci obdobné okno, v jakém se zrovna nacházíte, a podívat se na hodnoty atributů tohoto atributu.

Slovo private, které informaci o typu a názvu uvozuje, oznamuje, že atribut je soukromým majetkem instance, ke kterému nemá nikdo cizí přístup. O jeho významu si podrobněji povíme v dalších kapitolách.

Page 70: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

70 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 70 z 433

Opravit Obdélník o1 má 6 atributů:

Atribut název uchovává odkaz na řetězec označující daný obdélník. Můžete si všimnout, že každý další obdélník má číslo ve svém názvu o jedničku větší. Tento atribut slouží především k tomu, abyste mohli při ladění programu snáze poznat, o který obdélník se jedná.

Atributy xPos a yPos uchovávají pozici instance, přesněji pozici levého horní-ho rohu opsaného obdélníku. Jejich hodnotu zjistíme posláním zpráv int getX() a int getY() a nastavujeme ji posláním zprávy void setPozice(x,y).

Ve škole jste se nejspíš setkali pouze s opsanou kružnicí. Opsaný ob-délník je něco obdobného – myslíme jím nejmenší obdélník, do kterého se celý obrazec vejde.

Atributy šířka a výška uchovávají informace o rozměru dané instance – u na-šich tvarů jsou to opět informace o šířce a výšce opsaného obdélníku.

Atribut barva uchovává odkaz na objekt, který nese všechny potřebné infor-mace o barvě daného objektu.

Mění-li se stav objektu (v našem případě mění-li se jeho poloha, velikost nebo barva), mění se i hodnoty jeho atributů. BlueJ zobrazované hodnoty atributů in-stancí průběžně aktualizuje.

Nechte okno otevřené a zkuste požádat instanci, do jejíchž útrob nahlí-žíte, aby se někam posunula. Přesvědčte se, že se po splnění tohoto pří-kazu změní hodnoty atributů uchovávajících informaci o pozici instance. Zkuste vyvolat další metody a podívejte se, jak ovlivňují hod-noty jednotlivých atributů.

Atributy třídy – statické atributy Atributy, které nám prohlížeč doposud zobrazoval, jsou atributy příslušné instan-ce. Svoje atributy však může mít i celá třída. Atributy třídy pak všechny její in-stance sdílejí.

Page 71: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 71

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 71 z 433

Atributy třídy bývají často označovány jako statické atributy, protože jsou v programu uvozeny slovem static.

Zůstaneme-li u naší analogie, můžeme říci, že atributy třídy (šuplí-ky) jsou na pevném místě (v továrně), kam se na jejich obsah chodí jed-notlivé instance dívat nebo jej měnit. Tyto atributy bychom proto mohli označit za statické, protože se nehýbají. Naproti tomu atributy instancí si každá instance „vozí s sebou“.

Zobrazení okna s atributy třídy dosáhnete dvěma cestami:

V diagramu tříd zadáte v místní nabídce příslušné třídy příkaz Prohlížet.

V okně prohlížeče instancí stisknete tlačítko Ukázat atributy třídy.

V obou případech odpoví BlueJ otevřením okna s atributy příslušné třídy.

Obrázek 2.17 Okno prohlížeče atributů třídy

Požádáte-li třídu o nahlédnutí do jejích atributů před tím, než po ní bu-dete cokoliv chtít, najdete tam jenom nuly a prázdné odkazy (null). Je to proto, že třída ještě nebyla inicializovaná.

Třída se inicializuje až ve chvíli, kdy jí pošlete nějakou zprávu nebo ji požádáte o vytvoření instance. Než vám třída vaše přání splní, rychlese inicializuje a od té chvíle začnou mít hodnoty jejích atributů smysl.

Opravit Jak ukazuje obr. 2.17, třída Obdélník má tři atributy:

Atribut IMPLICITNÍ_BARVA obsahuje odkaz na barvu, kterou bude mít vytvo-řený obdélník v případě, že jej vytvoříte pomocí konstruktoru, jemuž neza-

Page 72: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

72 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 72 z 433

dáváte požadovanou barvu jako parametr, tj. konstruktoru, který takovýto parametr nemá.

Atribut krok obsahuje velikost implicitního kroku, o kterou se obrázek obdél-níka posune po plátně v případě, zavoláme-li některou z bezparametrických posunových metod. Jeho velikost zjišťujeme a nastavujeme metodami int getKrok() a void setKrok(velikost), o nichž jsme hovořili v kapitole Metody třídy na straně 64.

Atribut počet uchovává počet doposud vytvořených instancí. Odtud si kon-struktor bere číslo, které přidává za název třídy na konec hodnoty ukládané do atributu název.

Žáci se v tomto místě občas ptají, proč mezi statickými atributy je im-plicitní barva a proč tam nejsou implicitní poloha a implicitní rozměr. Důvody rozhodnutí autorů třídy pro zavedení či nezavedení toho či onoho atributu mohou být různé. Mne vedla k zavedení atributuIMPLICITNÍ_BARVA touha mít jeden atribut třídy objektového typu.

Řadu začátečníků překvapí, když zjistí, že některá třída nebo instance má mezi svými atributy odkazy na instance téže třídy. Zamyslíte-li se ale nad tím, musíte připustit, že nic z toho, co jsme si doposud o atribu-tech řekli, definici takovýchto atributů nezakazuje.

Možná o tom nevíte, ale od samého počátku s takovýmito třídamipracujete. Podíváte-li se na atributy třídy Barva nebo Směr, zjistíte, že je-jími atributy jsou právě odkazy na instance těchto tříd.

Obrátíte-li se na naši analogii s vozidly osazenými roboty, pak atributy, které jsou odkazy na instance vlastní třídy, jsou vlastně zásuvky s tele-fonními čísly těchto instancí. Instanci nic nebrání v tom, aby si vozila te-lefonní čísla (=odkazy) na jiné instance té samé třídy a zrovna tak v tom nic nebrání ani třídě.

Jak jsem již řekl, atributy třídy jsou pro všechny instance společné. Nezávisle na tom, odkud okno prohlížeče atributů třídy otevřete, otevře se vždy jedno a to sa-mé okno. Budete-li mít proto vytvořené dvě instance a požádáte-li v obou z nich o zobrazení statických atributů, tak se při druhé žádost žádné nové okno neotevře, ale pouze se aktivuje dříve otevřené okno.

Page 73: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 73

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 73 z 433

1. Vyzkoušejte reakci hodnoty atributu počet na vytvoření další in-stance.

2. Vyzkoušejte reakci hodnoty atributu krok na změnu zadanou volá-ním metody void setKrok(velikost).

3. Ověřte, že se při dalších žádostech o atributy třídy znovu aktivuje dříve otevřené (a doposud nezavřené) okno.

2.9 Přímé zadávání hodnot parametrů objektových typů

Při zadávání hodnot parametrů objektových typů nemusíme být odkázáni pouze na to, jestli se nám podaří umístit do zásobníku odkazů odkaz na objekt, který chceme předat jako parametr. I objektové parametry můžeme zadávat přímo, i když je to trochu těžší, ale opravdu jen trochu.

Abychom mohli odkaz na jakýkoliv objekt zadat přímo jako hodnotu para-metru, musíme mít šanci se k němu dostat. Při tom můžeme využít jednou ze dvou skutečností:

třída či instance má veřejný atribut, který na tento objekt odkazuje,

existuje metoda, která vrací odkaz na požadovanou instanci.

Veřejné atributy Jednodušší případ nastane ve chvíli, kdy víme, že odkaz na příslušný objekt lze získat z některého veřejného atributu třídy (atributu, u nějž nám prohlížeč pro-zradí, že je public). Pak stačí do příslušného vstupního pole zadat název příslušné třídy následovaný tečkou a názvem vyhlédnutého atributu (řekneme si třídě o odkaz na její veřejný atribut). Nesmíte přitom zapomenout na nutnost dodržování velikosti písmen.

Podívejme se např. na atributy třídy Směr. Zadejte v její nabídce příkaz Prohlížet a případně si zvětšete okno tak, abyste veřejné atributy třídy viděli všechny. Prohlížeč nám ukáže, že tato třída má 16 veřejných atributů třídy, z nichž polovina se jmenuje plným názvem daného směru a druhá polovina jeho zkratkou (viz obr. 2.18).

Page 74: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

74 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 74 z 433

Obrázek 2.18 Přehled veřejných atributů třídy Směr

Rozhodneme-li se proto vytvořit na souřadnicích [100;100] trojúhelník otočený na východ a pojmenovat jej tv, můžeme zadat parametry zprávy podle obrázku 2.19 – do pole pro zadání směru uvedeme Směr.V nebo Směr.VÝCHOD.

Page 75: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 75

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 75 z 433

Obrázek 2.19 Přímé zadání směru

Obdobně bychom postupovali i v případě, když bychom takto chtěli zadat někte-rou z devíti povolených barev – i ty jsou totiž veřejnými atributy třídy Barva.

Odkazy vrácené po zaslání zprávy Při zadávání odkazů na instance objektových datových typů můžeme využít i hodnot, které nám vrátí volání některých metod.

Víme například, že všechny instance geometrických tvarů umějí reagovat na zprávu Barva getBarva() a vrátit svoji barvu. Musí to umět i právě vytvořený trojúhelník. Postup zadání takto získané hodnoty odkazu je velmi podobný tomu, který jsme použili před chvílí při zadávání odkazu na atribut:

1. Napíšeme název toho, na koho se obracíme. Voláme-li metodu třídy, napíše-me název třídy, voláme-li metodu instance, napíšeme název odkazu na tuto instanci.

2. Za název přidáme tečku.

3. Za tečku napíšeme název metody, kterou voláme, následovaný seznamem parametrů v kulatých závorkách. Nemá-li metoda parametry, budou závorky

Page 76: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

76 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 76 z 433

prázdné. Má-li metoda parametry, musíme uvést ve správném pořadí se-znam jejich hodnot oddělených čárkami.

Vyzkoušejme si to. Vytvořme na souřadnicích [50;50] čtverec o straně 50 bodů, který bude mít stejnou barvu, jako před chvílí vytvořený trojúhelník tv. Zavoláme proto konstruktor obdélníku a do kolonky pro zadání barvy uvedeme tv.getBarva() (viz obr. 2.20).

Obrázek 2.20 Zadání odkazu na objekt prostřednictvím volání metody

Obdobně bychom postupovali i v případě, když bychom chtěli zadat barvu pro-střednictvím zasláním zprávy Barva getBarva(názevBarvy) třídě Barva. Tato zprá-va vyžaduje zadání parametru, kterým je název požadované barvy. A protože název barvy je text, musíme jej zadat v uvozovkách. Kdybychom proto chtěli ten-to způsob získání barvy použít při kreslení dalšího zeleného čtverce, zadali by-chom barvu

Barva.getBarva( "zelená" );

Okno konstruktoru, kterým žádáme o vytvoření tohoto čtverce (je stejný jako mi-nulý, pouze je o 100 bodů níže), si můžete prohlédnout na obrázku 2.21.

Page 77: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 77

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 77 z 433

Obrázek 2.21 Zadání odkazu voláním metody s parametrem

2.10 Shrnutí – co jsme se naučili Shrňme si, co jsme se v kapitole dozvěděli:

OOP chápe okolní svět jako svět objektů, které sdružujeme do tříd.

Každý objekt je instancí nějaké třídy. Termíny objekt a instance jsou synony-ma.

Objekty mezi sebou komunikují prostřednictvím zpráv.

Objektově orientovaný program je v nějakém programovacím jazyce zapsaný popis tříd, jejich instancí a zpráv, které si objekty mezi sebou posílají. Reakci na zaslanou zprávu má na starosti speciální část programu, označovaná jako metoda.

V jazyku Java vytváříme nové objekty zasláním zprávy s klíčovým slovem new následovaným názvem třídy a seznamem parametrů v kulatých závor-kách. Tím se vyvolá tzv. konstruktor.

Konstruktory jsou speciální metody zodpovědné za správné vytvoření in-stancí svých tříd.

Page 78: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

78 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 78 z 433

O vyhrazení paměťového místa pro vytvářený objekt a o jeho úklid poté, co přestaneme objekt potřebovat, se stará správce paměti.

Zavoláním konstruktoru získáme odkaz na objekt.

Program nikdy nepracuje s objektem, ale vždy pouze s odkazem na objekt.

Odkazy, třídy, zprávy a další objekty, s nimiž v programu pracujeme, ozna-čujeme identifikátory.

Identifikátory mohou obsahovat písmena (včetně písmen s diakritikou nebo např. japonských znaků), číslice a znaky „_“ (podtržítko) a „$“ (dolar). Nesmí začínat číslicí.

V identifikátorech se rozlišují velká a malá písmena.

Zprávy můžeme zasílat nejenom jednotlivým instancím, ale také celé třídě.

Zasláním zprávy můžeme požadovat nejenom provedení nějaké akce, ale ta-ké např. vrácení hodnoty.

Každá hodnota, kterou v programu použijeme, musí mít definován svůj da-tový typ.

Datové typy dělíme na primitivní a objektové.

Abychom si mohli vracené hodnoty prohlédnout, otevře BlueJ v reakci na za-slání zprávy požadující vrácení hodnoty dialogové okno, v němž vracenou hodnotu zobrazí.

Vrací-li metoda hodnotu objektového typu, můžeme v dialogovém okně klepnout na příslušný záznam a pak požádat buď o prohlédnutí dané instan-ce nebo o předání odkazu na ni. Při předání odkazu se tento uloží do zásob-níku odkazů.

Zprávy mohou mít parametry. Každý parametr má (stejně jako ostatní druhy dat) vždy definován svůj datový typ.

Objekty si pamatují svůj stav prostřednictvím atributů.

Vedle atributů instancí mohou být definovány i atributy třídy, které všechny instance dané třídy sdílí.

V místní nabídce třídy můžeme BlueJ požádat o zobrazení atributů této třídy.

Máme-li odkaz na instanci, můžeme BlueJ požádat o zobrazení jejích atributů a atributů její třídy.

Page 79: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 2: Pracujeme s třídami a objekty 79

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 79 z 433

Odkazy na instance, které jsou veřejnými atributy tříd, můžeme zadávat jako parametry i přímo zadáním názvu dané třídy následovaného tečkou a ná-zvem příslušného atributu.

Odkazy na instance objektových typů lze získávat i voláním metody vracející potřebný odkaz.

Nově zavedené termíny (abecedně) Tato kapitola byla nabitá novými termíny. Abyste si je mohli zopakovat, tak jsem vám je tu vyjmenoval. Budete-li si chtít některý z nich připomenout, najděte si v rejstříku stránku, kde je poprvé použit. argument atributy instancí atributy třídy boolean členské proměnné datový typ, typ dceřiná třída diagram tříd false halda hlavička konstruktoru hlavička metody identifikátor instance int (typ) konstruktor metoda metoda třídy návratová hodnota new (operátor) parametr správce paměti stav objektu String (typ) true vnitřní proměnné výstupní hodnota metody

Page 80: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

80 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 80 z 433

3. Vytváříme vlastní třídu

Kapitola 3 Vytváříme vlastní třídu

☯ Co se v kapitole naučíme V minulé kapitole jsme si s objekty hráli a ukazovali si, jak co funguje. V této kapitole začneme opravdu programovat a napíšeme své první řádky. Nezačneme však od nuly, ale budeme rozšiřovat projekt, s nímž jsme se seznámili v minulé kapitole.

Předem se omlouvám, že tato kapitola bude trochu delší, protože se v ní budeme postupně seznamovat se zápisem všech konstrukcí, které jsme si v minulé kapitole vysvětlili. Budeme se v ní proto věnovat prak-ticky pouze kódu, tj. tomu, jak to či ono zapsat.

Od příští kapitoly bych chtěl již obě části výkladu vyvážit. Vždy si budeme chvíli vyprávět o třídách, objektech a jejich vlastnostech, a pak si ukážeme, jak to, co jsme se právě naučili, zapsat do programu.

Page 81: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 81

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 81 z 433

Pro práci s touto a příští kapitolou si otevřete projekt 03_Třídy_A. Přípona A v názvu souboru značí, že se jedná o výchozí projekt dané lekce. Vedle něj budete potřebovat ještě projekt 03_Třídy_Z, který oproti projek-tu 03_Třídy_A obsahuje třídy, které přidáme nebo vytvoříme v průběhu třetí a čtvrté kapitoly.

Kromě toho v něm najdete zdrojový kód třídy Strom, v různých fá-zích rozpracovanosti tak, jak ji budeme v této kapitole postupně tvořit.Jednotlivé etapy přípravy jsou definovány jako třídy s názvem zakon-čeným číslem kapitoly a písmenem. Ve třetí kapitole tak vzniknou po-stupně třídy Strom_3a, Strom_3b a Strom_3c, v dalších kapitolách přidáme další.

Nový projekt má oproti minulému trochu přeuspořádané třídy v di-agramu tříd, aby se v něm lépe zobrazily vazby k třídám, které v prů-běhu této lekce sami nadefinujete. Kromě toho v něm přibyla pomocná třída P obsahující některé pomocné metody, které se nám budou hodit.

Jak jsem sliboval v první kapitole, uděláme drobnou terminologickou změnu. Java realizuje poslání zprávy prostřednictvím zavolání odpoví-dající speciální funkce, které říkáme metoda. Od této chvíle proto budu velmi často používat místo termínu posílání zpráv termín volání metod.

Aby těch úvodních poznámek nebylo málo, přidám ještě jednu. V prů-běhu dalšího textu budu velmi často říkat, že něco definujeme nebo de-klarujeme. Mezi těmito termíny je drobný rozdíl, který občaszačátečníkům uniká:

Budu-li říkat, že něco deklaruji, znamená to, že někde veřejně vyhla-šuji, jaké má dané něco vlastnosti nebo se zavazuji, že splním to, co dané něco požaduje. Řeknu-li naopak, že něco definuji, znamená to, že dané něco v pro-

gramu zavádím, nechávám tomu přidělit paměť, přiřadit počáteční hodnotu apod.

Ještě se k této problematice v některých speciálních případech vrá-tím.

Page 82: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

82 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 82 z 433

3.1 První vlastní třída Říkali jsme si, že základem všeho jsou objekty a ty že jsou instancemi svých tříd. Abychom mohli vytvořit objekt, musíme mít nejprve definovanou třídu, jejíž in-stancí objekt bude. Zkusíme si proto vytvořit novou, prázdnou třídu. Postup je jednoduchý:

1. Stiskněte vlevo tlačítko Nová třída (viz obr. 3.1).(V tuto chvíli je jedno, jsou-li ostatní třídy přeloženy nebo ne.)

Obrázek 3.1 Vytvoření nové třídy

2. V následně otevřeném dialogovém okně zadejte název třídy, který musí od-povídat pravidlům pro tvorbu identifikátorů. Protože chceme vytvořit prázdnou třídu, nazvěte ji příhodně Prázdná.

Page 83: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 83

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 83 z 433

Obrázek 3.2 Zadání názvu vytvářené třídy

3. Přepněte přepínač pod vstupním polem do poslední polohy nazvané Prázdná třída a své zadání potvrďte. BlueJ pak přidá právě vytvořenou třídu do dia-gramu tříd.

Obrázek 3.3 Přidání nové třídy do diagramu tříd

4. Podívejte se na disk do složky, ve které máte uloženy soubory projektu, a přesvědčte se, že se zde objevil nový soubor nazvaný Prázdná.java. Tento sou-bor označujeme jako zdrojový soubor a budete do něj za chvíli zapisovat program, v němž budete definovat požadované chování třídy a jejích instan-cí.

Zdrojový soubor tříd, které budeme v prostředí BlueJ vytvářet, se bude vždy jmenovat stejně jako vytvářená třída (a to včetně velikosti jednot-livých znaků) a bude mít příponu java. Pokud byste chtěli vytvářet zdrojové soubory jinde a jinak, musíte tuto konvenci dodržet.

Page 84: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

84 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 84 z 433

V diagramu tříd je obdélník představující novou třídu prozatím vyšrafován, ale my již víme, že šrafování znamená jen to, že třída ještě není přeložena. Stiskněte proto tlačítko Přeložit a BlueJ vám během chvilky třídu přeloží a připraví k použití.

Nyní již můžeme s nově vytvořenou třídou pracovat. Protože jsme ji však prozatím nic nenaučili, nemáme ji z čeho zkoušet. Můžeme sice vytvořit její in-stanci, ale ta nebude nic smysluplného umět. Nebudeme tedy nad ní dlouze bádat a podíváme se jí pěkně na zoubek.

3.2 Zdrojový kód třídy Vybavení třídy potřebnými schopnostmi je na nás. Má-li třída a její instance něco umět, musíme to nejprve zapsat do jejího zdrojového kódu, který je uložen ve zdrojovém souboru, což je textový soubor, jenž se automaticky vytvořil při vytvo-ření třídy (v našem případě to byl soubor Prádná.java).

Abychom do tohoto souboru mohli něco zapsat, musíme jej nejprve otevřít. Můžeme toho dosáhnout dvěma způsoby:

poklepáním na třídu v diagramu tříd,

zadáním příkazu Otevřít v editoru v místní nabídce třídy (viz obr. 3.4).

Obrázek 3.4 Příkaz o otevření zdrojového kódu třídy v editoru

Vyberte si postup, který je vám sympatičtější a otevřete editor se zdrojovým kó-dem třídy Prázdná. Text, který uvidíte (viz obr. 3.5), vygeneroval BlueJ v okamži-ku, kdy jste jej požádali o vytvoření nové třídy. Je to téměř prázdná definice třídy, do které budete vpisovat váš program. Jinými slovy: BlueJ za vás napsal to, co byste stejně museli napsat.

Page 85: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 85

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 85 z 433

Obrázek 3.5 Okno zabudovaného editoru

Prázdná třída To, co BlueJ připravil, je nejjednodušší možná definice třídy, která vypadá násle-dovně: public class Prázdná {}

Vysvětleme si nyní význam jednotlivých částí této definice:

public Klíčové slovo public oznamuje, že třída je veřejná a že s ní proto může pracovat kdokoliv. Teoreticky je sice možné definovat třídu i bez použití klíčového slova public, ale tuto možnost nyní využívat nebudeme a situ-ace, kdy to může být vhodné, si probereme až o několik kapitol později.

class Klíčové slovo class oznamuje, že za ním následuje definice třídy. Časem se naučíme ještě jiná slova ohlašující definici třídy.

Prázdná Za klíčovém slovem class následuje název neboli identifikátor třídy – musí proto splňovat příslušná pravidla (viz pasáž Pravidla pro tvorbu identifikátorů v jazyce Java na straně 46). Kromě toho musí platit, že název veřejné třídy musí být totožný s názvem souboru, v němž je uložen její zdrojový kód, a to včetně velikosti jednotlivých znaků. (Z toho logicky vyplývá, že v jednom souboru může být zdrojový kód nejvýše jedné ve-řejné třídy.) Soubor se zdrojovým kódem musí mít příponu java.

{ } Všechny doposud probrané části tvoří hlavičku definice třídy, podle které překladač pozná její základní charakteristiky. Za hlavičkou násle-duje tělo definice třídy uzavřené ve složených závorkách. Protože je na-še definice dosud prázdná, je prázdný i obsah závorek. S výjimkou dvou přesně specifikovaných příkazů (budeme o nich mluvit později) musí být v jazyku Java vše ostatní, tj. atributy i metody, definováno v těle tří-dy.

Page 86: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

86 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 86 z 433

V rozebírané definici je dvojice složených závorek na stejném řádku ja-ko definice třídy, kdežto v okně editoru je každá závorka na samostat-ném řádku. Java na uspořádání textu do řádků nehledí. Konec řádkupro ni má stejný význam jako mezera. Programy proto uspořádávámetak, abychom se v nich co nejlépe vyznali.

Poté, co zadáte výše popsanou minimální definici třídy, stiskněte tlačítko Přeložit (je úplně vlevo – viz obr. 3.6). BlueJ se „zamyslí“ a pokud jste neudělali chybu, tak dole pod editovaným textem napíše:

Třída byla úspěšně přeložena – žádné syntaktické chyby

Obrázek 3.6 Po úspěšném překladu vypíše editor zprávu

Tím jste dokončili svoji první definici třídy. Můžete se nyní vrátit do okna projek-tu a vyzkoušet si, že vaše nová třída doopravdy funguje a je např. schopna vytvá-řet nové instance.

Současně se také můžete podívat na disk a přesvědčit se, že se ve složce s projektem objevil vedle souboru Prázdná.java (v tom je, jak víte, vámi napsaný zdro-jový kód) ještě soubor Prázdná.class, v němž je uložen přeložený bajtkód. Kromě to-ho zde naleznete ještě soubor Prázdná.ctxt, ale to je pomocný soubor prostředí BlueJ, který pro svoji práci nepotřebujete (pokud jej smažete, BlueJ si jej při příštím pře-kladu dané třídy vytvoří znovu).

Soubor s příponou class, v němž je uložený přeložený bajtkód, bývá označován jako class-soubor příslušné třídy.

Page 87: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 87

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 87 z 433

☺ Kdybyste chtěli svůj projekt stěhovat na jiný počítač, stačí, když na nějpřenesete pouze soubory s příponou java. Budete-li chtít, aby v přene-seném projektu zůstalo zachováno také uspořádání tříd v diagramutříd, musíte přenést také soubor bluej.pkg. Ostatní soubory si BlueJ bez problémů a bez zdržování vytvoří, takže se s nimi přenášet nemusíte.

☺ Zdrojový soubor je obyčejný textový soubor. Pokud byste jej otevřeli vlibovolném textovém editoru (např. v programu Poznámkový blok), zjis-títe, že obsahuje pouze zadaný text, který můžete v tomto editoru klid-ně upravovat. Jenom dejte pozor na to, abyste jej neupravovali v době,kdy máte v prostředí BlueJ otevřen projekt, do nějž program patří. To by se vám editor a BlueJ pohádaly.

Obrázek 3.7 Přeložená prázdná třída je funkční

Implicitní konstruktor V minulé kapitole jsme si říkali, že vytvoření nové instance má na starosti speciál-ní funkce (metoda), které se říká konstruktor. My jsme však žádnou takovou me-todu nevytvořili, tak kde se vzala?

Page 88: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

88 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 88 z 433

Konstruktor musí mít každá třída. Pokud proto programátor pro třídu žádný konstruktor nedefinuje, domyslí si překladač, že mu bude stačit ten nejjednodušší a definuje jej pro ni sám. Tento konstruktor označujeme jako implicitní.

Jakmile programátor definuje jakýkoliv vlastní konstruktor, překladač se již nenamáhá a implicitní konstruktor nepřidává. Jinými slovy: jakmile programátor definuje ve třídě první konstruktor, musí je již definovat všechny, protože překla-dač již za něj nic nedoplní.

3.3 Odstranění třídy Prázdná třída, kterou jsme před chvílí vytvořili, může sloužit opravdu jen k výu-kovým účelům. V praxi bychom pro ni asi žádné uplatnění nenašli. Využijeme ji proto ještě k tomu, abychom si na ni ukázali, jak je možno třídu z diagramu tříd odstranit.

Postup je jednoduchý: v místní nabídce rušené třídy zadáte příkaz Odstranit. Protože odstranění třídy je přece jenom operace, která má na celý projekt zásad-nější dopad, tak se vás BlueJ pro jistotu nejprve zeptá, jestli to s tím odstraněním třídy myslíte vážně. Jakmile svůj úmysl potvrdíte, odstraní nejenom zdrojový soubor třídy, ale i všechny zmínky o tom, že třída byla někdy součástí projektu.

Obrázek 3.8 Před odstraněním třídy vyžaduje BlueJ potvrzení

Možná vás překvapí, že po žádosti o odstranění třídy sice třída z diagramu tříd zmizí, avšak její instance v zásobníku odkazů zůstane. Je to proto, že žádost o od-stranění třídy je vlastně žádostí o odstranění příslušných souborů z disku. Avšak od chvíle, kdy jste vytvořili instanci třídy, má počítač všechny potřebné informace také v operační paměti, odkud je odstraníte pouze tak, že restartujete virtuální stroj.

Page 89: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 89

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 89 z 433

3.4 Přejmenování třídy Zkuste si vytvořit prázdnou třídu ještě jednou. Je opravdu jednoduchá, takže by vám to nemělo dělat žádný problém.

Nyní začneme třídu vylepšovat a definujeme ji tak, aby její instance vytvářely na plátně nějaký obrázek. Pro začátek začneme s něčím opravdu jednoduchým – vytvořme např. třídu Strom, jejíž instance nakreslí na plátno symbolický listnatý strom s korunou a kmenem.

Začneme tím, že třídu přejmenujeme. BlueJ nám v tom pomůže. Změníme-li totiž v souboru, v němž je uložen zdrojový kód, název veřejné třídy, změní BlueJ při ukládání tohoto souboru potřebným způsobem i název souboru.

Můžete si vše hned vyzkoušet: změňte název třídy Prázdná na Strom. Nemusí-te ji ani překládat – k tomu, aby BlueJ zareagoval, bohatě stačí třídu uložit (stisk-nete CTRL+S nebo v editoru zadáte příkaz Třída → Uložit). BlueJ pak okamžitě přejmenuje příslušnou třídu v diagramu tříd a jak se můžete přesvědčit i pomocí nějakého správce souborů, změní příslušně i název zdrojového souboru na disku a smaže případné související soubory navázané na původní název.

Page 90: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

90 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 90 z 433

Obrázek 3.9 Po uložení souboru s přejmenovanou třídou

BlueJ tuto třídu v diagramu tříd okamžitě přejmenuje

Mezi programátory v Javě se ustálila konvence, podle které se názvytříd definují vždy s velkým počátečním písmenem a používají tzv. vel-bloudí notaci, při níž každé slovo několikaslovného názvu začíná vždy velkým písmenem – např. NázevTřídyZNěkolikaSlov.

Restartování virtuálního stroje naštěstí neznamená restartování celého počí-tače (většinou☺) – je to poměrně jednoduchá operace. Stačí zadat klávesovou zkratku CTRL+SHIFT+R, nebo (pokud si ji např. nepamatujete) vyvolat místní na-bídku Indikátoru činnosti (to je ta „pruhovaná tyčka“ nad levým okrajem zásobníku odkazů) a v ní zadat příkaz Restartovat VM (viz obr. 3.10).

Page 91: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 91

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 91 z 433

Obrázek 3.10 Restartování virtuálního stroje z místní nabídky Ukazatele činnosti

3.5 Bezparametrický konstruktor Takže skončíme již s prázdnou třídou a pokusíme se ji naučit něco užitečného. Nejprve bychom měli začít tím, že ji naučíme vytvářet smysluplné instance. K to-mu potřebujeme definovat konstruktor. Pusťme se tedy do toho.

Nejprve si musíme prozradit, jak vypadá definice konstruktoru. Pro jednodu-chost začneme konstruktorem bezparametrickým. Definice třídy s prázdným bez-parametrickým konstruktorem bude vypadat následovně: 1 2 3 4 5 6

public class Strom { public Strom() { } }

Projděme si nyní uvedený zdrojový kód a podívejme se znovu na význam a umís-tění jednotlivých částí řádek za řádkem:

1. Hlavička třídy, kterou již známe.

Page 92: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

92 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 92 z 433

2. Otevírací složená závorka označuje začátek těla třídy.

Budeme ji psát na samostatný řádek za hlavičku.

Všechny texty uvnitř těla třídy budeme pro přehlednost odsazovat. Veli-kost odsazení bývá většinou nastavena na 2 až 4 znaky – já budu použí-vat 4 znaky, ale vy si můžete nastavit odsazení vlastní.

3. Hlavička konstruktoru. Vidíte, že se podobá hlavičce třídy.

I tato hlavička je uvozena modifikátorem public, kterým oznamujeme, že s konstruktorem může pracovat každý.

Za modifikátorem public následuje jméno třídy, které je zároveň názvem konstruktoru.

Za názvem konstruktoru následuje seznam parametrů v závorkách. U bezparametrického konstruktoru je tento seznam prázdný.

4. Tělo konstruktoru je (stejně jako tělo třídy) uzavřeno ve složených závorkách.

Stejně jako u tříd budeme otevírací složenou závorku psán na samostat-ný řádek.

Stejně jako u těla třídy budeme vnitřek těla konstruktoru odsazovat.

5. Konec těla konstruktoru.

Závorku uzavírající tělo konstruktoru budeme psát na samostatný řádek.

Uzavírací závorku budeme zarovnávat pod otevírací závorku těla kon-struktoru.

6. Konec těla třídy.

Stejně jako u konstruktoru i u třídy budeme její uzavírací závorku psát na samostatný řádek.

Uzavírací závorku těla třídy budeme zarovnávat pod příslušnou otevíra-cí závorku.

Na konci minulé podkapitoly jsme si řekli, že instance čerstvě přejmenované třídy Strom nakreslí na plátno jednoduchý listnatý strom s korunou a kmenem. Kdyby-chom jej měli nakreslit „ručně“, asi bychom vytvořili zelenou instanci elipsy, kte-rou bychom použili jako korunu, a doplnili ji dole červeným (hnědá v seznamu není) obdélníkem, který by představoval kmen. (Zkuste si to!)

Při tvorbě programu budeme postupovat naprosto stejně, pouze místo zadá-vání příkazů z nabídky tyto příkazy napíšeme do těla konstruktoru. Naše první smysluplná definice třídy tedy může vypadat např. následovně:

Page 93: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 93

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 93 z 433

1 2 3 4 5 6 7 8

public class Strom { public Strom() { new Elipsa( 0, 0, 100, 100, Barva.ZELENÁ ); new Obdélník( 45, 100, 10, 50, Barva.ČERVENÁ ); } }

Co jsme udělali? V těle konstruktoru jsme zadali dva příkazy. Na pátém řádku programu jsme zavolali konstruktor elipsy, kterému jsme předali následujících 5 parametrů:

vodorovnou a svislou souřadnici jsme nastavili na 0,

šířku a výšku jsme nastavili na 100,

barvu jsme definovali prostřednictvím předání odkazu na atribut ZELENÁ tří-dy Barva.

Obdobně jsme posupovali i na šestém řádku v případě obdélníka, který předsta-vuje kmen. Zde jsme si pouze museli nejprve spočítat vodorovnou souřadnici kmene, aby byl umístěn přesně pod středem koruny. Má-li mít kmen např. šířku 10 bodů, musí být umístěn 5 bodů před tento střed – a to jsme udělali.

Zkušenější programátoři často umisťují otevírací závorku těla třídy,konstruktoru atd. na konec předchozího řádku. Ušetří tím jeden řádek, ale ztíží tím kontrolu toho, že každá uzavírací závorka má svoji odpo-vídající otevírací závorku a naopak. Vřele vám proto doporučuji, abystealespoň zpočátku určitě umisťovali jak otevírací, tak uzavírací závorkuna samostatné řádky. Vyhnete se tak řadě „oblíbených“ chyb.

Všimněte si, že každý příkaz je ukončen středníkem. Na to musíte dát pozor. Za-pomenutý středník patří k jedněm z nejčastějších začátečnických chyb (naštěstí je to chyba lehce odhalitelná a odstranitelná).

Na druhou stranu vám prozradím, že Java (na rozdíl např. od jazyku Visual Basic) nijak nelpí na tom, na kolika řádcích bude příkaz rozprostřen nebo jestli umístíte několik příkazů na jeden řádek. Při psaní programu se proto snažte uspořádat jednotlivé příkazy tak, aby byl program co nejpřehlednější. K této otáz-ce se ještě několikrát vrátím.

Page 94: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

94 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 94 z 433

Java nerozlišuje tzv. bílé znaky (mezera, tabulátor, konec řádku, konec stránky). Kdekoliv může být v programu mezera, tam může být který-koliv jiný bílý znak – např. právě konec řádku.

Přeložte nyní upravený program a podívejte se na diagram tříd. BlueJ zaregistro-val, že jsme v těle třídy použili třídy Obdélník a Elipsa a okamžitě k nim natáhl šipky závislostí (viz obr. 3.11).

Obrázek 3.11 BlueJ odhalil používání tříd a natáhl automaticky šipky závislostí

Požádejte nyní v diagramu tříd třídu Strom, aby vytvořila novou instanci stromu. Po jejím vytvoření bude plátno vypadat podle obr. 3.12.

Page 95: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 95

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 95 z 433

Obrázek 3.12 Plátno se stromem

3.6 Ladění Ne vždy se nám podaří napsat program bez chyby. Pravdou je spíše opačné tvr-zení – téměř každý program obsahuje nějakou chybu. Chyby, které se v progra-mech objevují, bychom mohli rozdělit do tří skupin:

Syntaktické chyby jsou prohřešky proti pravidlům zápisu jazyka. Tyto chy-by jsou svým způsobem „nejpříjemnější“, protože je odhalí již překladač a poměrně jasně nás na ně upozorní. Syntaktickou chybou by v našich prvních programech bylo např. vynechání některé ze závorek, případně přidání nad-bytečné závorky, vynechání středníku, zkomolení názvu volaného konstruk-toru, atributu či třídy apod.

Běhové chyby jsou chyby, na které se nepodaří přijít během překladu a které se projeví až za běhu programu a vedou k nějakým výjimečným situacím. Mezi takovéto chyby patří např. dělení nulou, pokus o použití objektu, který ještě nebyl vytvořen apod. Tyto chyby jsou sice nepříjemné, ale při důklad-ném testování programu bychom je měli mít šanci všechny (nebo alespoň skoro všechny) odhalit a opravit.

Logické chyby (často se setkáte s označením sémantické1 chyby) jsou chyby v logice programu, které zapříčiní, že program nedělá přesně to, co má. Ty se

1 Sémantický = významový.

Page 96: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

96 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 96 z 433

umějí nejlépe maskovat a dá většinou nejvíce práce je odhalit a opravit. Sé-mantickou chybou by v našem předchozím programu např. bylo, kdyby-chom špatně spočítali souřadnice nebo velikost obrazců a výsledný strom by pak vypadal divně, pokud by vůbec vypadal jako strom.

Vyzkoušíme si nyní všechny druhy chyb a podíváme se, jak na ně bude počítač (a následně i my) reagovat.

Syntaktické chyby Jak jsem již řekl, odhalování a opravování syntaktických chyb je nejjednodušší (i když i ony nám jsou schopny občas připravit pěkný rébus), takže s jejich zkouše-ním začneme.

Udělejte nejprve v programu chybu – odstraňte např. středník za prvním pří-kazem – a zkuste program znovu přeložit.

BlueJ váš program opět nejprve uloží a pak jej začne překládat. Protože však najde v programu chybu, překlad nedokončí a místo očekávané hlášky Třída byla úspěšně přeložena – žádné syntaktické chyby zobrazí v dolním informačním poli chybo-vou zprávu ';' expected (viz obr. 3.13). Navíc ve zdrojovém textu zvýrazní řádek, v němž chybu odhalil.

Obrázek 3.13 BlueJ ohlásil chybu při překladu

Tato zpráva je sice psána anglicky, ale klepnutím na otazník na pravém okraji in-formačního pole otevřete dialogové okno, které vám většinou chybové hlášení přeloží a často i česky vysvětlí – viz obr. 3.14. Tato vysvětlení sice nejsou k dispozici pro všechny chyby, nicméně nejčastější chyby takto vysvětleny jsou.

Page 97: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 97

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 97 z 433

Obrázek 3.14 Pro většinu chyb má BlueJ připraveno české vysvětlení

Připravte se na to, že na řadu chyb počítač přijde na jiném místě programu, než na kterém k chybě došlo. Chybu totiž neoznámí v místě, kde jsme ji udělali, ale v místě, kde ji odhalil. V našem předchozím příkladě mu samozřejmě nevadilo, že jsme středník neudělali hned za voláním konstruktoru. Jak jsem již řekl, Javě je poměrně jedno, jak program uspořádáme do řádků. Program proto chybění středníku objeví až v okamžiku, kdy narazí na další operátor new aniž by byl předchozí příkaz uzavřen středníkem – a k tomu dojde právě na následujícím řádku.

Jakmile chybu opravíte, BlueJ váš program přeloží a budete jej moci znovu používat.

Zkuste vyrobit další syntaktické chyby tak, že odstraníte nebo přidátedo programu nějaký znak a podívejte se, jak na ně bude BlueJ reagovat.

Běhové chyby Běhové chyby jsou v pořadí nepříjemnosti chyb uprostřed. Překladač je sice neod-halí již při překladu, avšak chyba se „iniciativně“ projeví při běhu programu – většinou tím, že program zkolabuje. Abychom takovéto chyby odhalili, musí pro-gram krizovým místem proběhnout. Musíme si proto připravit sadu testů, které postupně prozkoumají všechny zákoutí programu a umožní nám takovéto chyby najít dřív, než na ně přijde zákazník.

Podívejme se, jak v případě takovýchto chyb reaguje BlueJ. Zkuste např. v prvním příkazu nahradit první nulu výrazem 0/0 (v programech se pro dělení používá znak „/“) a požádejte opět o překlad.

Page 98: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

98 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 98 z 433

Lepší překladače by sice takovouto triviální chybu objevily již při pře-kladu, ale BlueJ je program optimalizovaný pro výukové účely, takže seo takovéto fajnovosti nepokouší a program přeloží.

Požádáte-li nyní třídu Strom o vytvoření instance, nepodaří se jí to. BlueJ proto otevře textový editor, zobrazí ve zdrojovém kódu místo, kde došlo k chybě, a ve spodním informačním poli vypíše, o jakou chybu se jedná. V našem případě zde napsal (viz obr. 3.15):

ArithmeticException: / by zero

Tuto zprávu bychom mohli přeložit tak, že vznikla výjimka při aritmetické opera-ci. Na druhém řádku je pak specifikováno, že se jedná o dělení nulou.

Obrázek 3.15 Reakce prostředí na běhovou chybu

Takováto chyba je však příliš primitivní a v programu byste ji asi neudělali. Zku-síme nějakou rafinovanější. Odstraňte z příkazu dělení nulou a zkuste v třetím pa-rametru, v němž zadáváme požadovanou šířku obdélníku, nahradit původní hodnotu nulou. Příkaz pak bude mít podobu: new Elipsa( 0, 0, -3, 100, Barva.ZELENÁ );

Takováto podoba běhové chyby je daleko pravděpodobnější. Je nenápadná a k podobnému přehlédnutí může snadno dojít. Výsledek je však stejný: dojde k vý-jimečné situaci a program se zastaví v místě, kde k ní došlo (viz obr. 3.16). V uve-deném případě pak vypíše:

IllegalArgumentException: Parametry nemají povolené hodnoty: x=0, y=0, šířka=-3, výška=0

Page 99: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 99

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 99 z 433

Problém je v tom, že tentokrát k chybě dojde v konstruktoru elipsy, který odmítne vytvořit elipsu nulové šířky (to se ještě dá ze zprávy vyčíst). Vy však potřebujete vědět, kde chyba doopravdy vznikla. V našem jednoduchoučkém programu je to jasné, ale ve složitějších programech je to docela problém.

Naštěstí Java nabízí prostředky, jak to zjistit. Jejich použití ale patří k vyšší škole programátorského umění, takže si o nich povíme až později.

Obrázek 3.16 Konstruktor elipsy odmítá vytvořit instanci nulové šířky

Logické (sémantické) chyby Základním problémem logických chyb je to, že si je nemusíme dlouho uvědomo-vat. Možná někteří z vás slyšeli o aféře s mikroprocesory Intel Pentium, které v jednom z triliónů případů ne zcela přesně vydělily dvě čísla. Nic nehavarovalo, jenom výsledek měl někde na desátém desetinném místě chybu. Na takovou chy-bu se dá přijít opravdu jen náhodou. V tom právě spočívá záludnost logických chyb.

Uděláte-li v programu chybu, která je hned viditelná (např. umístíte-li špatně kmen vůči koruně stromu), je to v pohodě. Uděláte-li však chybu, která se nave-nek nijak výrazně neprojevuje, avšak o to citelněji škodí, je to obrovský problém.

I logické chyby se snažíme odhalit sadou propracovaných testů, avšak u slo-žitějších programů můžeme těžko zaručit, že jsme otestovali opravdu vše a že program žádné logické chyby neobsahuje.

V dalším textu se vám budu snažit průběžně dávat tipy na to, jak logické chyby odhalovat, ale dopředu vám říkám, že pro jejich odhalení žádné zaručené pravidlo neexistuje.

Page 100: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

100 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 100 z 433

3.7 Konstruktor s parametry Doufám, že jsem vás předchozí podkapitolou o chybách moc nevyděsil a že mů-žeme pokračovat a dále vylepšovat náš první program.

Strom jsme sice nakreslili, ale vytvoříme-li druhý, nebudeme jej moci od prv-ního odlišit, protože se budou překrývat. Vytvoříme proto druhý konstruktor, který bude mít parametry, jež nám umožní specifikovat, kam se má vytvářený strom nakreslit. Vzpomeneme si, že parametry se píší do kulatých závorek za ná-zev konstruktoru a že před každým parametrem musí být nejprve uveden jeho typ. Je-li parametrů více, oddělují se čárkou.

Konstruktor umožňující nakreslit strom na zadané souřadnice by mohl vy-padat následovně: 1 2 3 4 5

public Strom( int x, int y ) { new Elipsa ( x, y, 100, 100, Barva.ZELENÁ ); new Obdélník( x+45, y+100, 10, 50, Barva.ČERVENÁ ); }

Proberme si předchozí definici podrobněji:

1. V hlavičce konstruktoru se objevil neprázdný seznam parametrů. Všimněte si, že u každého parametru je uveden nejprve jeho typ a za ním identifikátor, jehož prostřednictvím se program na parametr v těle konstruktoru odvolává.

2. Otevírá tělo konstruktoru.

3. Tam, kde jsme v bezparametrickém konstruktoru dosazovali hodnoty vodo-rovné a svislé souřadnice konstruované elipsy, dosazujeme nyní hodnoty pa-rametrů x a y. Zásada je taková, že kde chceme dosadit hodnotu parametru, tam vložíme jeho název.

4. V dalším příkazu jsme použití parametrů ještě vylepšili a dosadili jsme je přímo do výrazů. Jako vodorovnou souřadnici obdélníka dosadíme číslo, kte-ré je o 45 větší než hodnota parametru x a za svislou souřadnici číslo, které je o 100 větší než hodnota parametru y.

5. Konec těla konstruktoru.

Page 101: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 101

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 101 z 433

☺ Pokud jste se v dřívějších dobách setkali s programováním, možná vás nutili používat pouze procedury a funkce, které jste před tímv programu definovali. Java na vás takovéto požadavky neklade. Napořadí, v jakém definujete jednotlivé konstruktory jí nezáleží. Pořadí, vjakém definujete jednotlivé typy konstruktorů (a následně i atributů ametod), záleží zcela na vás.

Přidejte výše uvedený kód do těla třídy vedle bezparametrického kon-struktoru a třídu přeložte. Vraťte se pak do prostředí BlueJ a vytvořtena různých místech plátna několik instanci třídy Strom. Přesvědčte se, že se stromy nakreslily opravdu tak, jak bylo vaším záměrem.

Vytvořte některou z tříd ČinkaO (O=Obdélník), ČinkaK (K=Kruh), Hvězda, Panáček, Panenka, jejichž instance se na plátně zobrazí obdobně, jako na obrázku:

Konstruktor this Oba konstruktory, které jsme doposud vytvořili, jsou si velice podobné. To je u konstruktorů velmi časté, protože mají velice podobné úkoly. Programátoři jsou ale lidé líní (proto se také dali na programování) a neradi píší něco dvakrát. Navíc je opakované psaní stejného kódu i nebezpečné, protože když budeme chtít v tomto kódu později něco změnit, musíme si pamatovat, kde všude jsme jej použi-li, všechna místa navštívit a do kódu příslušnou změnu zanést. Stačí na jedno mís-to zapomenout nebo v něm opravu zanést trochu jinak a chybička je na světě.

Java naštěstí nabízí způsob, jak se opakovanému psaní těla konstruktoru vy-hnout – je jím použití klíčového slova this následovaného seznamem parametrů.

Page 102: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

102 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 102 z 433

Takto použité klíčové slovo this zastupuje jiný konstruktor téže třídy – který to je, to si překladač odvodí z počtu a typu zadaných parametrů.

POZOR! Takovéto volání jiného konstruktoru musí být úplně prvním příkazem těla konstruktoru. Před ním smí být již pouze mezera nebo komentář.

Využijeme-li klíčové slovo this, mohla by definice třídy Strom vypadat např. ná-sledovně: 1 2 3 4 5 6 7 8 9

10 11 12 13 14

public class Strom { public Strom() { this( 0, 0 ); } public Strom( int x, int y ) { new Elipsa ( x, y, 100, 100, Barva.ZELENÁ ); new Obdélník( x+45, y+100, 10, 50, Barva.ČERVENÁ ); } }

Vyzkoušejte si, že i po této změně definice bezparametrického konstruktoru vše pracuje podle očekávání.

Zkusme nyní naši definici ještě vylepšit a definovat konstruktor, který by umožňoval zadat i výšku a šířku konstruovaného stromu. U tohoto stromu se bu-deme snažit dodržet pravidlo, že výška kmene je polovina výšky koruny a šířka kmene je zhruba desetina šířky koruny. Tím se nám sice definice trochu zkompli-kuje, ale na druhou stranu získáme obecnou definici, která dokáže vytvářet stro-my libovolné velikosti a v libovolné pozici.

Protože bychom po prostém přidání této definice měli opět zdvojený kód, tak v konstruktoru se souřadnicemi opět použijeme volání konstruktoru s využitím klíčového slova this.

Máme-li definovat konstruktor, který dokáže nastavit i výšku a šířku stromu, musíme vědět, jak se v programu zadává násobení a dělení. Prozradím vám, že naprostá většina programovacích jazyků používá pro násobení symbol „*“ (hvěz-dička) a pro dělení symbol „/“ (lomítko). Nejinak je tomu v Javě.

Abychom měli přehled aritmetických operátorů kompletní, tak vám prozra-dím, že Java zavádí ještě operátor %, který vrací zbytek po dělení svých dvou ope-randů (např. 5 % 3 je 2).

Page 103: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 103

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 103 z 433

Výsledný kód vypadá následovně: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20

public class Strom { public Strom() { this( 0, 0 ); } public Strom( int x, int y ) { this( x, y, 100, 150 ); } public Strom( int x, int y, int šířka, int výška ) { new Elipsa ( x, y, šířka, 2*výška/3, Barva.ZELENÁ ); new Obdélník( x+9*šířka/20, y+2*výška/3, šířka/10, výška/3, Barva.ČERVENÁ ); } }

Při seznamování s příkazy jsem vám říkal, že Java nelpí na tom, aby byl příkaz na jednom řádku. V předchozím programu jsem toho využil apříkaz na řádku 16 bezostyšně zalomil na dva řádky, protože se mi na jeden řádek nevešel. V programátorském světě bývá dobrým zvykemnedělat řádky delší než 80 znaků. Budu se této zásady držet i v této učebnici.

Prvé dva konstruktory v programu samy nic nedefinují a nechávají za sebe praco-vat jiný konstruktor, kterému pouze předají vhodné hodnoty parametrů. Všimně-te si, že prvý konstruktor pověřuje druhý a druhý pověřuje třetí. Při zavolání bezparametrického konstruktoru se proto nejprve zavolá druhý konstruktor, kte-rému se předají nulové souřadnice a ten místo sebe zavolá třetí konstruktor, kte-rému předá převzaté souřadnice a přidá k nim informace o požadované implicitní velikosti stromu.

S takovýmto řešením se v programech setkáte často – je to vlastně dotažená zásada, že se ve zdrojovém kódu nemá pokud možno žádná část programu opa-kovat. V prvém konstruktoru jsou proto definovány implicitní souřadnice a v druhém pak implicitní velikost. Kdybychom hned v prvém konstruktoru volali třetí konstruktor, museli bychom uvést implicitní souřadnice i zde. Když bychom se je pak později rozhodli změnit, museli bychom si pamatovat, že jsou na dvou místech a na obou místech je pak také opravit.

Page 104: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

104 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 104 z 433

Opět platí: v takto jednoduchém programu byste to asi nepřehlédli (i když kdo ví…), ale ve složitějších programech by už bylo nebezpečí přehlédnutí veliké. Znovu proto připomínám: Naučte se psát i jednoduché programy podle zásad, které je třeba dodržovat při psaní programů složitých. Za prvé se zlozvyky veli-ce těžko odnaučují a kromě toho nikdy nevíte, jak vám váš program v průběhu doby naroste.

Definujte výše popsané obecnější konstruktory s parametry i pro svojitřídu, kterou jste vytvořili podle úkolu na konci podkapitoly o bezpara-metrickém konstruktoru.

3.8 Testování V kapitole o chybách jsme si říkali, že chceme-li minimalizovat počet chyb ve vý-sledném programu, musíme pro něj pečlivě připravit sadu testů, kterým program před odevzdáním podrobíme.

TDD – vývoj řízený testy Moderní programování jde ale ještě dál. V současné době se stále více prosazuje myšlenka definovat nejprve sadu testů a teprve pak psát testovaný program. Při psaní programu se pak stačí soustředit pouze na to, aby výsledný program prošel napsanými testy.

Jakmile program testy projde, programátor se zamyslí, jak by jej měl dále vy-lepšit, zase napíše testy a opět se snaží upravit program tak, aby testy prošel.

Tato metodika již získala své jméno: Test Driven Development – vývoj říze-ný testy. Používá se pro ni zkratka TDD1. Budu-li se na ni chtít v textu někdy od-volat, tak tuto zkratku použiji.

Postup doporučovaný TDD je možná na první pohled nesmyslný, ale při bližším zkoumání zjistíte, že má řadu výhod:

V průběhu psaní testů si programátor ujasní, co přesně má vytvářený pro-gram dělat.

1 O této metodice vyšla velice zajímavá knížka Beck K.: Test-Driven Development By Example,

Addison Wesley, 2003. Český překlad Vývoj programů řízený testy, Grada, 2004 (ISBN 80-247-).

Page 105: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 105

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 105 z 433

Při psaní programu může kdykoliv spustit předpřipravenou sadu testů a průběžně na nich kontrolovat, kolik jich už proběhlo a kolik práce mu ještě zbývá.

Ve chvíli, kdy proběhnou všechny testy, je programátor s prací hotov a ne-musí již přemýšlet nad tím, jestli v jeho programu nejsou chyby.

Zkušenost ukazuje, že máte-li připravenou sadu testů před začátkem programo-vání, je produktivita vaší práce výrazně vyšší – nejspíš proto, že jste klidnější, pro-tože jste si jisti, že vám tyto testy kryjí záda a že můžete v každé situaci vyzkoušet, jestli se program po úpravě chová lépe nebo hůře. Budeme se proto této zásady snažit držet i v této učebnici.

Bohužel, nebudu se jí moci držet pořád (i když bych rád), protože by rozsah knihy narostl do neúměrné velikosti. Jednou za čas si však vývoj části programu za pomoci této metodiky ukážeme.

Autoři prostředí BlueJ jsou také přesvědčeni o důležitosti testů, a proto do programu přidali několik nástrojů, které jejich přípravu a spouštění zjednodušují. Tyto nástroje se stanou použitelnými po zaškrtnutí políčka Zobrazit nástroje pro testo-vání v dialogovém okně pro nastavení vlastností programu (viz pasáž Konfigurace BlueJ na straně 411). Postupně se budeme učit je využívat.

Testovací třída K testování využíváme speciální třídy, které jsou vybaveny některými zvláštními vlastnostmi. Budeme jim říkat testovací třídy.

Abychom testovací třídy na první pohled odlišili od ostatních tříd, vybarvuje je BlueJ v diagramu tříd jinou barvou a označuje je tzv. stereotypem, což je popi-sek uzavřený ve «dvojitých lomených uvozovkách». Tento stereotyp obsahuje text unit test naznačující, že se jedná o test jednotky. O tom, co je to jednotka, přitom rozhoduje programátor – může to být třída, část třídy a nebo naopak celý projekt. Do testovací třídy pak umisťujeme všechny testy prověřující danou jednotku.

Testovací třídu můžeme vytvořit např. tak, že v místní nabídce třídy, kterou chceme testovat, zadáme příkaz Vytvořit testovací třídu. BlueJ pak vytvoří novou třídu, jíž název složí z názvu testované třídy a slova Test, a umístí ji pod třídu, v jejíž místní nabídce jsme vytvoření testovací třídy zadali (voz obr. 3.17).

Page 106: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

106 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 106 z 433

Obrázek 3.17 Testovací třída třídy Strom

Testovací třída se bude testované třídy držet jako klíště. Kamkoliv testovanou tří-du posunete, posune se testovací třída za ní. Testovací třídu sice můžete přesu-nout na libovolné jiné místo diagramu tříd, ale jakmile pak hnete s testovanou třídou, testovací třída k ní opět přiskočí (vyzkoušejte si to).

Přípravek Velmi často je výhodné provádět každý z testů se stejnou výchozí sadou objektů. K tomuto účelu nabízí BlueJ možnost definovat tzv. přípravek, což je sada objek-tů, které se vytvoří před spuštěním každého testu, doplněná případně o nastavení nějakých okrajových podmínek.

Definice přípravku je jednoduchá: odkazy na všechny objekty, které chcete do přípravku umístit, nejprve připravíte do zásobníku odkazů a pak v místní na-bídce příslušné testovací třídy zadáte příkaz Zásobník odkazů −> testovací přípravek. BlueJ pak všechny odkazy ze zásobníku odkazů odebere a umístí je do testovacího přípravku.

Můžeme si to hned vyzkoušet.

Page 107: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 107

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 107 z 433

1. Požádejte třídu Strom, aby vytvořila následující objekty:

strom1 vytvořený bezparametrickým konstruktorem,

strom2 vytvořený na souřadnicích x=0, y=150,

strom3 o šířce 100 a výšce 90 vytvořený na souřadnicích x=100, y=100.

2. Požádejte třídu StromTest, aby obsah zásobníku odkazů uložila jako přípra-vek. Třída si pak všechny odkazy zapamatuje a restartuje virtuální stroj (a tím zároveň smaže odkazy ze zásobníku odkazů).

3. Požádejte nyní třídu StromTest, aby naplnila zásobník odkazů z přípravku. Třída vytvoří všechny instance, na které byly před tím odkazy v zásobníku odkazů a do zásobníku umístí odkazy na tyto instance. Odkazy budou mít i stejná jména, jen jejich pořadí se možná bude lišit, ale na tom beztak nezáleží.

Při tvorbě přípravku si musíte uvědomit, že BlueJ sleduje vaše chování od posled-ního restartu virtuálního stroje, tj. např. od chvíle, kdy jste naposledy vytvořili, resp. přeložili nějakou třídu. Budete-li si před vlastním vytvořením přípravku hrát a vytvářet a mazat různé objekty, BlueJ po žádosti o uložení obsahu zásobní-ku do přípravku uloží nejenom všechny vaše dosavadní hrátky. BlueJ se totiž ne-vytváří přípravek na základě toho, co najde v zásobníku odkazů, ale na základě toho, jaké zprávy jste komu poslali od chvíle, kdy jste restartovali virtuální stroj – to nastane např. při překladu libovolné třídy.

Chcete-li proto mít jistotu, že sledování vaší činnosti začne právě teď, požá-dejte BlueJ o restartování virtuálního stroje (kdo zapomněl, jak se to dělá, tak se může podívat na obr. 3.10 na straně 91).

Úprava obsahu přípravku Někdy se stane, že si dodatečně uvědomíme, že by se nám hodilo, aby přípravek vypadal trochu jinak. Není problém – stačí „nasypat“ obsah přípravku do zásob-níku, přidat nebo odebrat příslušné instance a výslednou podobu zásobníku opět uložit do přípravku.

Opět si to hned zkusíme:

1. Restartujte virtuální stroj.

2. Požádejte třídu StromTest o naplnění zásobníku odkazů.

3. Požádejte třídu Strom o vytvoření instance strom4 na souřadnicích x=200, y=0, o rozměrech šířka=100, výška=200.

4. Požádejte třídu StromTest o převedení zásobníku odkazů na přípravek.

Page 108: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

108 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 108 z 433

5. Třída otevře dialogové okno, v němž vás upozorní na to, že daný test již má deklarovaný přípravek a zeptá se vás, jestli jej chcete nahradit (viz obr. 3.18). Odpovězte stiskem tlačítka Nahradit.

Obrázek 3.18 Svůj úmysl předefinování testovacího přípravku musíte potvrdit

6. Od této chvíle má třída deklarován nový přípravek, který můžete ihned vy-zkoušet. Po jeho aktivaci by mělo plátno vypadat obdobně jako na obr. 3.19.

Obrázek 3.19 Vzhled plátna generovaný upraveným testovacím přípravkem

Výše popsaným způsobem má smysl upravovat testovací přípravek tehdy, chce-te-li do něj zahrnout ještě nějaký další objekt. Není moudré se takto interaktivně pokoušet z přípravku nějaký objekt odstranit. V takovém případě je výhodnější otevřít zdrojový kód třídy a zařazení objektu do zásobníku odkazů změnit ručně.

Ještě se k této problematice vrátíme, až toho budeme vědět dost na to, aby-chom si mohli prohlédnout zdrojový kód testovací třídy a ukázat si, jak jej může-me sami upravovat. Prozatím se spokojíme s tím, že testovací přípravek je naším testem, který nám umožní ověřit, že náš program funguje správně.

Page 109: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 109

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 109 z 433

Vytvořte testovací třídy s přípravky i pro vaše vlastní třídy a vyzkou-šejte, že fungují.

3.9 Deklarace atributů Nyní již tedy umíme umístit náš strom tam, kam potřebujeme, a umíme jej vytvo-řit tak velký, jak potřebujeme. Teď bychom jej ještě potřebovali rozhýbat.

Kdybychom ale zůstali u dosavadních definic konstruktorů, tak bychom toho s našimi stromy moc dělat nemohli. Jejich koruna a kmen se sice nakreslí, ale pak s nimi nic dalšího dělat nemůžeme. Abychom je mohli ještě někdy o něco požádat (např. o to, aby se přesunuly), musíme si někde zapamatovat odkaz na ně.

Cokoliv si instance potřebuje zapamatovat, ukládá do atributů. Můžete si je představit jako schránky, do nichž mohou některé metody něco uložit a jiné pak pracovat s tím, co je zde uloženo.

Atributy deklarujeme skoro stejně jako parametry. Neuvádějí se však v žádné hlavičce, ale v těle třídy vně těl jejích konstruktorů a metod. To je totiž jediné mís-to, kde na ně mohou všechny metody dané třídy „vidět“, aby je mohly použít.

Bývá dobrým zvykem nerozstrkat deklarace atributů po celé definici třídy, ale umístit je pohromadě buď na její začátek nebo na její konec. My je budeme umísťovat na počátek – je to častější a navíc mi to připadá i logičtější.

Deklarace atributu sestává z následujících částí:

1. Nepovinné modifikátory, které blíže specifikují některé vlastnosti definova-ného atributu. Za chvilku vám o nich povím více.

2. Typ atributu, tj. název primitivního typu nebo název třídy, na jejíž instanci atribut ukazuje.

3. Název atributu – podle konvencí začíná název atributu malým písmenem. U několikaslovných názvů se používá velbloudí notace. Název atributu by měl jasně označovat, jaká informace je v atributu uložena.

4. Nepovinné přiřazení počáteční hodnoty sestávající z rovnítka a hodnoty, kterou chceme, aby měl atribut před svým prvním použitím.

5. Závěrečný středník.

Page 110: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

110 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 110 z 433

Deklarace atributů je současně i jejich definicí (rozdíl mezi deklarací adefinicí viz terminologická poznámka na str. 81.), protože se jim při té příležitosti vždy vyhradí místo v paměti a přiřadí počáteční hodnota.Atributy, kterým v deklaraci nepřiřadíte počáteční hodnotu vy, za vás překladač „vynuluje“. Nemůže se tedy stát, že byste v programu začali používat atribut, v němž by bylo nějaké nedefinované smetí. (Je-li v něm nějaké smetí, museli jste si je tam dát sami. ☺)

Modifikátory přístupu Jak jsem před chvílí řekl, před vlastní deklaraci atributu můžeme přidat modifiká-tory. Nejpoužívanější z nich jsou modifikátory přístupu, které specifikují, kdo všechno smí s daným atributem pracovat (kdo k němu smí přistoupit). Své modi-fikátory přístupu mohou mít nejenom atributy, ale i konstruktory a celé třídy. Modifikátory přístupu definují přístupová práva k daným objektům (třídám, kon-struktorům, atributům, …).

Prozatím jsem se setkávali pouze s modifikátorem public, který oznamoval, že příslušnou třídu či konstruktor mohou používat všichni. Atributy jsou však považovány za soukromou věc každé třídy, ve které nemá nikdo cizí mít šanci se hrabat. Je-li třída ochotna některé informace o svých atributech zveřejnit, dělá to většinou jinými metodami, než uvolněním jejich přístupových práv.

Vše, co chceme deklarovat jako soukromou věc třídy, označujeme modifiká-torem private. Dohodněme se, že atributy budeme primárně označovat jako pri-vate. Jiná přístupová práva jim budeme přiřazovat pouze ve výjimečných případech a měli bychom mít tuto změnu vždy nějak zdůvodněnu.

Jako soukromé můžeme označit nejenom atributy, ale také konstrukto-ry a metody. Učiníme tak v případě, kdy je daná metoda definována ja-ko pomocná a není žádný důvod pro to, aby ji ostatní třídy mohly používat.

Vylepšujeme Strom Vraťme se nyní zpět k našemu stromu. Budeme-li jej chtít posunout, mohli by-chom to udělat např. tak, že bychom o příslušný posun požádali jeho korunu (elipsa se přeci přesouvat umí) a poté kmen (obdélník se také umí přesouvat). Po-

Page 111: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 111

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 111 z 433

kud by si naše instance pamatovala odkazy na korunu a kmen, mohla by jim pak poslat zprávu, ve které by je požádala o to, aby se posunuly.

Doplníme proto náš program o atributy, v nichž si zapamatujeme odkaz na korunu a kmen, a při konstruování stromu do těchto atributů uložíme odkazy, které nám vracejí konstruktory. Upravená definice třídy bude vypadat následov-ně: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21 22 23

public class Strom { private Elipsa koruna; private Obdélník kmen; public Strom() { this( 0, 0 ); } public Strom( int x, int y ) { this( x, y, 100, 150 ); } public Strom( int x, int y, int šířka, int výška ) { koruna = new Elipsa ( x, y, šířka, 2*výška/3, Barva.ZELENÁ ); kmen = new Obdélník( x+9*šířka/20, y+2*výška/3, šířka/10, výška/3, Barva.ČERVENÁ ); } }

Všimněte si, že jsem oba atributy umístil na začátek třídy (jak jsem sli-boval). Dohodněme se dopředu, že naše třídy budou vždy začínat defi-nicí atributů, pokračovat definicí konstruktorů a končit definicemimetod.

Nyní třídu přeložíme a nahráním přípravku do zásobníku odkazů hned otestuje-me, že jsme úpravou programu nic nepokazili.

Požádejte nyní BlueJ o prohlédnutí kterékoliv z instancí v zásobníku. U každé se dozvíte, že má soukromý atribut koruna, který je typu Elipsa a soukromý atri-but kmen typu Obdélník. Klepnete-li na některý z nich, zvýrazníte jej a po stisku tlačítka Prohlížet si můžete prohlédnout jeho útroby, tj. jeho atributy.

Page 112: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

112 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 112 z 433

Obrázek 3.20 Prohlížeč nám ukáže atributy stromu

Doplňte o atributy i svoje třídy a vyzkoušejte je nahráním přípravku do zásobníku odkazů.

Možné důsledky zveřejnění atributů Jak jsem řekl, všechny atributy by měly být deklarovány jako soukromé. Ukažme si, co by se mohlo stát, kdyby tomu tak nebylo:

1. Změňte deklaraci koruny a označte ji jako veřejnou.

2. Třídu znovu přeložte.

3. Vytvořte její instanci např. bezparametrickým konstruktorem.

4. Podívejte se na vytvořenou instanci prohlížečem. Oproti stavu, který prohlí-žeč ukazoval před „publikováním koruny“ je nyní „živé“ i tlačítko Získat odkaz indikující, že můžete získat odkaz na daný objekt a umístit jej např. v zásobníku odkazů.

5. Stiskněte tlačítko Získat odkaz a novému odkazu dejte název koruna.

6. V zásobníku odkazů požádejte instanci koruna např. o to, aby se posunula vpravo.

7. Koruna stromu, na jejíž instanci odkaz vede, se poslušně přesune požadova-ným směrem. Můžete ji samozřejmě zadat jakýkoliv jiný příkaz (např. smaž) a ona poslechne.

Předpokládám, že možné důsledky zveřejnění atributů jsou z tohoto příkladu jas-né. Protože je atribut veřejný, může k němu přistupovat kdokoliv. Je úplně jedno, zda nevhodnou manipulací s atributem někdo poškodí vaši instanci záměrně ne-bo omylem – důsledkem bude poškozená instance.

Page 113: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 113

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 113 z 433

3.10 Syntaktické definice

Tato kapitola trochu odbočuje od vlastního výkladu. Chtěl bych vásv ní seznámit se způsobem zápisu syntaktických definic, pomocí nichž lze jednoznačně popsat, jak má vysvětlovaná konstrukce vypadat.Možná bude některým z vás připadat těžká. Pokud se vám bude zdát, že je příliš abstraktní, klidně ji přeskočte a vraťte se k ní později, až si budete chtít ujasnit, jak si máte nějakou syntaktickou definici „přelo-žit“.

Při popisu syntaxe programových konstrukcí (syntaxe = souhrn pravidel, jak kon-strukci zapsat) nebývá slovní popis optimálním způsobem vyjádření, protože se při něm často ztrácejí cenné informace v záplavě okolních slov. V programátorském světě se proto používají jiné druhy popisu. Jedním z nich jsou syntaktické definice. Ty budu používat i já.

V syntaktických definicích je třeba rozlišit, co se z nich má do programu „opsat“, co pouze zastupuje nějaký objekt a co je pouze pomocný vyjadřovací prostředek pro popis struktury dané konstrukce. V syntaktických definicích, které budu v tomto kurzu používat, uvedu vždy na prvním řádku tučně název popiso-vané konstrukce a na dalších, odsazených řádcích pak popíšu vlastní definici. V ní budu používat následující prvky:

tučně Pro ty části konstrukce, které se mají do výsledného programu opsat (např. pro složené závorky ohraničující tělo konstruktoru či třídy nebo pro čárku oddělující jednotlivé parametry v seznamu), budu používat podšeděné tučné neproporcionální písmo.

název Pro názvy prvků, které se v definici vyskytují a které jsou samy defino-vány jinde, budu používat kurzivu.

[ ] Hranaté závorky budou uzavírat volitelnou část konstrukce, tj. část, kte-rá se v konstrukci může, ale také nemusí vyskytovat.

… Výpustka (trojtečka) bude následovat za prvkem, který se může v konstrukci vyskytovat opakovaně.

{ } Složené závorky budou uzavírat skupinu prvků, s nimiž budu chtít v definici pracovat jako s celkem – např. za ně budu chtít vložit výpustku naznačující, že celá skupina se může opakovat.

¦ Dvojitá svislá čára („svislítko“) bude mít funkci nebo, která říká, že ve výsledné konstrukci se může objevit buď prvek vlevo od něj anebo

Page 114: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

114 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 114 z 433

prvek vpravo od něj. Ne však oba zároveň (to bychom museli zapsat ji-nak).

Definici konstruktoru bychom podle těchto pravidel zapsali následovně:

Konstruktor: [ modifikátor ]… název_třídy ( [ definice_parametru [, definice_parametru ]… ] ) { [ příkaz ]… }

Definice_parametru: typ název

Definici interpretujeme tak, že na počátku se může, ale nemusí vyskytovat modi-fikátor (zatím známe pouze modifikátor public, ale je jich víc). Modifikátorů zde dokonce může být uvedeno několik.

Za modifikátorem následuje název třídy, jejíž instance konstruktor vytváří, a za ním pak otevírací kulatá závorka uvozující seznam případných parametrů. Za ní může, ale nemusí být uvedena definice použitého parametru. Za definicí prv-ního parametru mohou být i definice dalších parametrů, ale každá z nich již musí být od předchozí definice oddělena čárkou.

Za seznamem parametrů napíšeme zavírací kulatou závorku a za ní otevírací složenou závorku uvozující tělo konstruktoru. V těle konstruktoru nemusí být nic, ale může tam být i několik příkazů. Tělo ukončí uzavírací složená závorka.

V definici konstruktoru se vyskytuje prvek definice_parametru. Ten vytvoříme tak, že uvedeme typ parametru a za ním jeho název.

Abychom si ukázali použití složených závorek a „svislítka“, rozebereme si ještě definici identifikátoru:

Identifikátor: { písmeno ¦ _ ¦ $ } [ písmeno ¦ _ ¦ } ¦ číslice ]…

Tato definice je však nešikovná a uvedl jsem ji pouze proto, abych v ní mohl pou-žít i složené závorky. V praxi bychom identifikátor definovali asi následovně: Identifikátor:

zobecněné_písmeno [ zobecněné_písmeno ¦ číslice ]… Zobecněné_písmeno:

písmeno ¦ _ ¦ $

Uvedená definice říká, že identifikátor musí začínat zobecněným písmenem, za ním může následovat libovolný počet zobecněných písmen a číslic.

Page 115: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 115

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 115 z 433

3.11 Definujeme vlastní metodu Jak jsem již dříve naznačil, instance a třídy reagují na zasílané zprávy tak, že spus-tí speciální podprogram, kterému budeme říkat metoda. Metoda definuje reakci instance či třídy na příslušnou zprávu. Programátoři proto již většinou ani neříka-jí, že poslali třídě či instanci zprávu, ale říkají, že zavolali její metodu.

My už jsme vlastně v předchozích podkapitolách vlastní metody definovali, protože konstruktor je také metoda, i když zvláštní. V této kapitole se však za-čneme věnovat metodám, které zvláštní nejsou.

Budu-li v dalším textu hovořit o metodách, budu tím myslet pouzestandardní metody a nikoliv konstruktory. Pokud bych náhodou hovo-řil o něčem, co je společné standardním metodám i konstruktorům, vý-slovně to uvedu.

Podívejme se nejprve, jak by měla taková definice standardní metody správně vypadat. Zápis syntaxe definice metody vypadá následovně:

Definice_metody: [ modifikátor ]… typ_návratové_hodnoty název ( [ definice_parametru [, definice_parametru ]… ] ) { [ příkaz ]… }

Přeložme si nyní tuto syntaktickou definici do „srozumitelštiny“. Metoda má (stejně jako třída) hlavičku a tělo. Hlavička sestává z následujících částí:

1. Nepovinné modifikátory – prozatím jsme se seznámili s modifikátory pří-stupu public a private. Ty označují, kdo všechno smí metodu volat (tj. kdo smí poslat příslušnou zprávu).

2. Typ návratové hodnoty. Tento typ musíme uvést i tehdy, když metoda nic nevrací – v takovém případě uvádíme typ void (prázdno).

3. Název metody. Pro názvy metod se používají stejné konvence jako pro názvy atributů, tj. začínají malým písmenem, přičemž se u několikaslovných názvů používá velbloudí notace.

4. Seznam parametrů v kulatých závorkách. Pro tento seznam platí naprosto stejná pravidla jako pro seznam parametrů konstruktorů, tj.:

U každého parametru musí být uveden nejprve jeho typ a za ním jméno, jehož prostřednictvím se budeme v těle metody odvolávat na hodnotu tohoto parametru.

Page 116: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

116 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 116 z 433

Je-li parametrů více, oddělují se jednotlivé parametry v seznamu čárka-mi.

Nemá-li metoda žádné parametry, musí se za jménem uvést prázdné zá-vorky.

5. Tělo metody uzavřené mezi dvojici složených závorek. Pro tělo metody platí:

Tělo metody sestává ze seznamu příkazů, který může být i prázdný, tj. tělo nemusí obsahovat žádný příkaz.

Jednotlivé příkazy není třeba nijak oddělovat. Překladač umí poznat, kde jeden příkaz končí a druhý začíná.

Podle konvencí píšeme ohraničující závorky každou na samostatný řá-dek a zarovnáváme je pod první znak hlavičky.

Podle konvencí odsazujeme příkazy uvnitř těla cyklu oproti hlavičce o dva až čtyři znaky.

Od této chvíle již nebudu v textu při zmínkách o metodách uvádět typnávratové hodnoty a pokud nebude hrozit nedorozumění, tak ani se-znam parametrů. Pouze v případě, kdy vás budu žádat, abyste z místní nabídky třídy v diagramu tříd nebo instance v zásobníku odkazů posla-li třídě či instanci zprávu, budu uvádět plný text příslušné položky.

První metodou, kterou bychom měli pro náš strom definovat, je metoda nakres-li(), protože ji většina ostatních metod bude potřebovat. Tato metoda má za úkol překreslit náš strom (např. tehdy, když jej někdo jiný smaže).

Její definice bude jednoduchá: nemá žádné parametry ani nic nevrací, takže bude mít jednoduchou hlavičku, a díky jednoduchosti naší instance i jednoduché tělo – prostě požádá instance tvořící korunu a strom, aby se překreslily: 1 2 3 4 5

public void nakresli() { koruna.nakresli(); kmen. nakresli(); }

V těle metody si všimněte, jak se v programu zapisuje příkaz k zaslání zprávy ně-jaké instanci: napíše se název odkazu na tuto instanci (my máme odkaz uložen v atributu – napíšeme tedy název tohoto atributu), za název se napíše tečka, za teč-ku název metody realizující reakci na danou zprávu následovaný kulatými zá-

Page 117: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 117

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 117 z 433

vorkami se seznamem hodnot předávaných parametrů (voláme-li bezparametric-kou metodu, budou závorky prázdné). Celý příkaz ukončíme středníkem.

V předchozím programu jsem v příkazu na řádku 4 schválně zarovnal volání metody pod obdobné volání na předchozím řádku, abyste viděli, že názvy a tečka nemusí být na sebe nalepené, ale může mezi nimi být i mezera a tím i jakýkoliv ji-ný bílý znak – např. konec řádku. To využijete v okamžiku, kdy se budete rozho-dovat, kde rozdělit příliš dlouhý příkaz.

Definujte k této metodě doplňkovou metodu smaž(), která nakreslenou instanci na plátně smaže.

Test vytvořených metod Metody jsme vytvořili, tak bychom je měli ihned otestovat. Ukážeme si, jak pro ně můžeme vytvořit automatizovaný test. Pomůžeme si přitom metodou souhlas, která je metodou třídy P. Tato metoda otevře dialogové okno, v němž nám položí předem zadanou otázku a pak vrátí volajícímu programu informaci o tom, jak jsem odpověděli.

Takže vzhůru do toho. Postupujte následovně:

1. Restartujte virtuální stroj, abyste měli jistotu, že vycházíte z počátečního sta-vu.

2. Požádejte třídu StromTest, aby z přípravku naplnila zásobník odkazů.

3. V místní nabídce třídy StromTest zadejte příkaz Vytvořit testovací metodu.

Obrázek 3.21 Zadání názvu vytvářeného testu

4. V následně otevřeném dialogovém okně zadejte název vytvářeného testu – např. NakresliSmaž, jak je tomu na obrázku 3.21, a své zadání potvrďte. Tím spustíte záznam akcí, které provedete. Navíc se na levém panelu červeně roz-svítí příznak záznam a pod ním se aktivují tlačítka Ukončit a Storno (viz obr. 3.22).

Page 118: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

118 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 118 z 433

Obrázek 3.22 Spuštěný záznam testu je indikován symbolem záznam

a aktivovanými tlačítky Ukončit a Storno

5. Pošlete postupně jednotlivým instancím zprávu smaž().

6. Pošlete třídě P (to je ta třída, která v tomto projektu přibyla) zprávu boolean souhlas(dotaz), ve které jako parametr uvedete text "Stromy smazány?" (neza-pomeňte na uvozovky).

Obrázek 3.23 Dotaz na to, zda se podařilo stromy opravdu smazat

Neobjeví-li se okno s dotazem, bude nejspíše překryto některým jiným oknem. V takovém případě postupně minimalizovat ostatní okna, až sehledané dialogové okno objeví. Mělo by být ve středu obrazovky.

Page 119: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 119

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 119 z 433

7. Zadaný řetězec se objeví jako výzva v následně otevřeném dialogovém okně (viz obr. 3.23). Stiskněte ANO, čímž potvrdíte, že stromy byly z plátna opravdu smazány (alespoň doufám, že k tomu došlo i u vás).

8. Po odpovědi na předchozí otázku vám BlueJ oznámí, jakou hodnotu třída P po zaslání zprávy vrátila (vrátila true, protože jsme odpověděli ANO) a zeptá se vás, má-li v příštích testech testovat vracenou hodnotu a pokud ano, tak jakou.

Obrázek 3.24 Oznámení vrácené hodnoty a dotaz na to, má-li příště vrácenou hodnotu testovat

9. V dialogovém okně zaškrtněte políčko Předpokládat že, čímž BlueJ požádáte, aby v testu ověřoval očekávanou návratovou hodnotu. Zároveň ověřte, že se bu-de testovat, jestli je návratová hodnota rovna true (jinými slovy, že při příš-tím testu potvrdíme, že stromy byly smazány).

10. Nyní pošlete postupně každé z instancí zprávu void nakresli(), čímž ji po-žádáme o to, aby se znovu nakreslila.

11. Znovu pošleme třídě P zprávu boolean souhlas(dotaz), avšak tentokrát s pa-rametrem "Stromy opět nakresleny?".

12. Opět odpovíme ANO (předpokládám, že se stromy správně nakreslili) a požá-dáme BlueJ, aby i při příštích bězích ověřoval kladnou odpověď.

13. Stiskem tlačítka Ukončit ukončíme zaznamenávání testu.

14. Restartujte virtuální stroj, čímž zavřete okno plátna a s ním i celou kreslící aplikaci.

Test je vytvořen a podíváte-li se do místní nabídky třídy StromTest, zjistíte, že v ní přibyla položka Testovat NakresliSmaž. Zadáte-li tento příkaz, spustí se právě vytvo-řený test znovu.

Spustíme jej a podíváme se, jak by vše probíhalo, kdyby nebylo všechno v pořádku. Zkuste např. odpovědět na první otázku NE.

Page 120: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

120 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 120 z 433

BlueJ zjistí, že na otázku bylo odpovězeno jinak, než jste zadali při zadávání testu, a ukončí test. Otevře pak okno s výsledky testů, kde vám tuto nepříjemnou zprávu oznámí. V horní části okna pak najdete seznam všech provedených testů (prozatím máme definován pouze jeden), přičemž u úspěšně ukončených testů naleznete zelené zaškrtnutí, u neúspěšných testů šedé X (to je náš případ). Spodní část je určena pro podrobnější informace o výsledku vybraného testu.

Dole zatím není nic, protože žádný test není vybrán. Klepněte na náš test a dole se zobrazí zpráva, oznamující, že bylo očekáváno true a obdrženo false a poté informace o tom, kde k dané chybě došlo (viz obr. 3.25). Zde se můžeme do-zvědět, že k chybě došlo ve třídě StromTest v její metodě testNakresliSmaž a že kritické místo, kde došlo k chybě, nalezneme ve zdrojovém souboru StromTest.java na řádku 72. To by nám mělo stačit k tomu, abychom zjistili, kdo je za chybu zod-povědný (teď nic zjišťovat nebudeme, protože to víme).

Obrázek 3.25 Okno s výsledky testů

Připravte podobný test i pro vaši vlastní třídu a ověřte s jeho pomocí, že je vše naprogramováno správně.

Nejprve testy, pak program? Jak jsem již řekl, testy bychom měli správně dělat před tím, než příslušnou meto-du definujeme. Nyní asi namítnete, že před tím, než metodu definujeme, ji nebu-deme moci vyvolat a připravit pro ni test.

Page 121: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 121

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 121 z 433

Pomoc je jednoduchá: vložte do těla třídy definované metody s prázdnými tě-ly a při tvorbě testu se tvařte, jako že tyto metody fungují. Tak, jak jim postupně budete definovat těla, budou příslušné testy postupně začínat správně chodit.

Můžeme si to hned vyzkoušet. Instance všech tří základních geometrických tříd se umějí na požádání posunout. Rozšiřme proto o toto umění i naši třídu Strom.

Nejprve do ni vložíme prázdné definice jednotlivých posunových metod (na pořadí nezáleží). 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21 22 23

public void posunVpravo() { } public void posunVlevo() { } public void posunVzhůru() { } public void posunDolů() { } public void posunVpravo( int n ) { } public void posunDolů( int n ) { }

Upravenou třídu přeložíme a připravíme test, který nazveme např. Posuny. V tom-to testu nejprve pošleme třídě P zprávu void zpráva(text) s parametrem "Následuje posun vpravo". Po obdržení této zprávy otevře dialogové okno, v němž vypíše text svého parametru a počká, až stisknete tlačítko OK. Dělám to proto, aby si testující uživatel mohl zapamatovat současnou pozici stromů a ověřit, že se opravdu po-sunuly vpravo.

Pak postupně požádáme všechny instance, aby se posunuly vpravo (ony sa-mozřejmě nezareagují, protože těla metod jsou prázdná, ale to nevadí). Poté po-šleme třídě P známou zprávu boolean souhlas(dotaz), které předáme jako parametr text "Posun vpravo vpořádku?\n\nNásleduje posun vlevo".

Page 122: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

122 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 122 z 433

V textu tohoto parametru bych vás chtěl upozornit na dvě dvojice zna-ků \n uprostřed textu. Tato dvojice znaků zařídí, že v místě jejího vý-skytu program odřádkuje. Protože jsou v textu dvojice dvě, odřádkujedvakrát, takže mezi dotazem a oznámením další akce bude vynechanýřádek – viz obr. 3.26).

Obrázek 3.26 Vložení dvojice znaků \n do řetězce způsobí přechod na nový řádek

Pak požádáte všechny instance, aby se posunuly vlevo a opět celou sérii zakončíte zprávou třídě P, která vyvolá potvrzovací dialogové okno. Totéž zopakujete pro posun vzhůru a nakonec i pro posun dolů.

Obdobný test bychom měli vytvořit i pro přesuny se zadanou vzdáleností. Ty vám ale prozatím odpustím. Později vám ukážu, jak můžete takovýto test doplnit přímým zásahem do kódu testovací třídy.

Tím je tedy vše připraveno a můžeme začít programovat.

V dalším textu vás již většinou při definici testů nebudu vést za ruku.Budu předpokládat, že si potřebné testy dokážete definovat sami a že si je budete opravdu definovat. Budete tak mít jistotu, že jste vše defino-vali správně a že vaše příští programy mohou právě vytvořený (a otes-tovaný) program bez obav používat. K přípravě testů se vrátím jen tehdy, budu-li se domnívat, že se na nich můžeme něco nového naučit.

Naprogramujte posunové metody a vyzkoušejte, že všechny pracujípodle očekávání.

Page 123: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 123

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 123 z 433

Někdy jsou věci složitější Odhaduji, že jste při programování posunových metod použili stejnou fintu, ja-kou jsme použili při definici metody kresli(), tj. požádat o požadovanou akci po-stupně korunu a kmen: 1 2 3 4 5

public void posunDolů() { koruna.posunDolů(); kmen .posunDolů(); }

Jestli jste však takto definovanou metodu posunDolů() testovali, dočkali jste se asi překvapení, protože plátno pak vypadalo jako na obr. 3.27 – posouvané stromy mají v korunách díry.

Obrázek 3.27

Důvod již známe, protože jsme si o něm povídali v minulé kapitole: při přesouvá-ní se obrazce nejprve smažou v původní pozici a pak se nakreslí v pozici nové. Je-nomže při posouvání kmene již byla namístě původního kmene koruna v nové pozici, takže jsme při mazání kmene odmazali i část nově umístěné koruny (v dů-sledku tohoto postupu také strom vlevo dole odmazal při přesunu kmen stromu vlevo nahoře, ale to není chyba).

Problém můžeme řešit dvěma způsoby:

napřed obě části stromu smazat a pak je požádat, aby se vykreslili v nové po-zici,

Page 124: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

124 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 124 z 433

po přesunu obou částí do nové pozice korunu ještě jednou překreslit.

Kdyby byl náš program příliš pomalý, asi bychom museli zvolit první možnost, při které se nic nekreslí dvakrát. Protože nás však čas výpočtu netlačí, zvolíme druhé řešení, které nám dá méně práce. Výsledná podoba metody je následující: 1 2 3 4 5

public void posunDolů() { koruna.posunDolů(); kmen .posunDolů(); koruna.nakresli(); }

Šťouralové zde možná namítnou, že jsme vše opravili pouze pro jedenstrom, ale budeme-li přesouvat několik stromů po sobě, budou pozdějipřesouvané stromy občas umazávat části stromů přesunutých dříve.

Bohužel, mají pravdu. V této části ještě nemáme dostatek nástrojů pro to, abychom mohli tento problém řešit (leda bych naprogramovalplátno jinak, ale já je mám takto jednoduché schválně).

Problém vyřešíme až ve druhé části, kdy se seznámíme s novýminástroji a kdy také začneme používat jiné plátno.

Použití metod vracejících hodnotu Prozatím jsme dosazovali za parametry pouze čísla, řetězce, hodnoty jiných pa-rametrů anebo vzorečky, kde jsme opět použili čísla nebo hodnoty parametrů. Ja-va nám ale nabízí ještě další možnost – použít návratovou hodnotu metody. Tu získáme tak, že danou metodu zavoláme na místě, kde chceme tuto hodnotu pou-žít.

Vyzkoušejme si to v definici metody setPozice(int,int), která umístí strom na pozici zadanou v parametrech. Umísťování koruny je jednoduché, protože ko-runa má stejné souřadnice jako celý strom. Problém ale nastane při umísťování kmene, jehož souřadnice budeme muset nejprve vypočítat z velikosti koruny.

Svislou pozici kmenu získáme relativně snadno – stačí k požadované svislé souřadnici přičíst výšku koruny. Vodorovnou souřadnici budeme muset trochu počítat. Nejsnadněji se k ní asi dostaneme, když rozdíl šířek koruny a kmene vy-dělíme dvěma (polovina tohoto rozdílu bude vlevo od kmene a polovina vpravo od něj): 1 public void setPozice( int x, int y )

Page 125: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 125

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 125 z 433

2 3 4 5 6 7

{ koruna.setPozice( x, y ); kmen .setPozice( x + (koruna.getŠířka() - kmen.getŠířka) / 2, y + koruna.getVýška() ); koruna.nakresli(); }

Při vykonávání této metody bude virtuální stroj postupovat následovně:

1. Zavolá metodu setPozice(int,int) instance, na kterou odkazuje atribut koruna a jako hodnoty parametrů ji předá hodnoty svých parametrů.

2. Začne se chystat zavolat tutéž metodu instance, na níž odkazuje atribut kmem(). K tomu ale potřebuje znát hodnoty parametrů.

3. Zavolá metodu getŠířka() instance, na kterou odkazuje atribut koruna, pak zavolá metody getVýška() instance, na níž odkazuje kmen, obě hodnoty ode-čte a vydělí dvěma. Výsledek přičte k hodnotě parametru x a takto spočtenou hodnotu připraví jako první parametr.

4. Zavolá metodu getVýška() instance, na kterou odkazuje atribut koruna, zís-kanou hodnotu přičte k hodnotě druhého parametru a výsledek připraví jako hodnotu druhého parametru.

5. Konečně zavolá metodu setPozice(int,int) instance, na kterou odkazuje atribut kmen() a předá jí spočtené hodnoty parametrů.

6. Zavolá metodu nakresli() instance, na níž odkazuje atribut koruna. Tím za-kryje část koruny, která mohla být odmazána při přesouvání kmene (pokud se nic neodmazalo, tak se koruna jen nakreslí dvakrát).

V příštích rozborech se již nebudu tak rozepisovat a místo „zavolá me-todu xyz() instance, na kterou odkazuje atribut abc“ budu psát rovnou „zavolá abc.xyz()“.

Definice metod vracejících hodnotu Metody vracející hodnotu už umíme použít, takže je nejvyšší čas, abychom se je také naučili definovat. Tyto metody se budou lišit od doposud definovaných me-tod ve dvou věcech:

místo typu void mají v hlavičce uveden typ hodnoty, kterou vracejí,

Page 126: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

126 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 126 z 433

před ukončení činnosti musí vrátit požadovanou hodnotu – k tomu slouží příkaz return, za který napíšeme výraz, jehož hodnotu bude metoda vracet.

V jazyku Java mohou metody vracet pouze jedinou hodnotu. Stejně jetomu i v převážné většině ostatních moderních, objektově orientova-ných jazyků. Budete-li potřebovat vrátit více hodnot zároveň, můžetepoužít obratu popsaného v kapitole Přepravka (Messenger) na straně 230.

Metodu getX(), která vrátí aktuální pozici stromu vykresleného danou instancí, bychom pak mohli definovat následovně: 1 2 3 4

public int getX() { return koruna.getX(); }

Stejně jednoduché jsou i metody getY() a getŠířka(). Metoda getVýška() je jen o maličko složitější, protože výšku stromu nestačí pouze převzít, ale musí si ji vypo-čítat. Za příkaz return proto uvede příslušný výraz: 1 2 3 4

public int getVýška() { return koruna.getVýška() + kmen.getVýška(); }

Parametry a návratové hodnoty objektových typů Prozatím jsme pracovali pouze s celočíselnými parametry a návratovými hodno-tami. Pro jistotu zde připomínám, že parametry i návratové hodnoty mohou mít libovolný typ.

Vyzkoušejme si třeba možnost nastavování a vracení barvy. U stromu není zvykem, aby měla koruna i kmen stejnou barvu. Nic nám ale nebrání definovat metody, které nastaví zvlášť barvu pro korunu a zvlášť barvu pro kmen. Stejně tak můžeme definovat metody, které na požádání tyto barvy vrátí. Pro korunu by jejich definice mohla vypadat následovně: 1 2 3 4 5

Public Barva getBarvaKoruny() { return koruna.getBarva(); }

Page 127: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 127

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 127 z 433

6 7 8 9

public void setBarvaKoruny( Barva nová ) { koruna.setBarva( nová ); }

V projektu 03_Třídy_Z najdete třídu Strom_3a, která obsahuje definice uvedené v této kapitole. Neobsahuje však řešení „domácích úkolů“. Kromě toho zde najdete i třídu Strom_3aTest, pomocí níž můžete třídu Strom_3a otestovat.

3.12 Přetěžování V minulé kapitole jsem vám říkal, že definujeme-li několik verzí konstruktorů ne-bo stejnojmenných metod, které se liší pouze počtem a/nebo typem svých parame-trů, označujeme to za přetěžování. Občas se mne žáci ptají, jak program pozná, kterou verzi přetíženého konstruktoru či metody má v reakci na danou zprávu použít. Prozradím vám, že klíčovou úlohu zde hrají typy parametrů. Mohli byste si to např. představit tak, že interně používá překladač jméno konstruktoru, které je složené z vlastního jména následované seznamem typů parametrů.

Vezměme si třeba obdélník – ten má čtyři konstruktory, které bychom podle předchozího pravidla mohli pojmenovat např. (oddělovací znak # jsem si vymys-lel sám):

Obdélník Obdélník#int#int Obdélník#int#int#int#int Obdélník#int#int#int#int#Barva

Obdobně je to i s metodami. Podíváme-li se např. na metody realizující odpověď na zprávu požadující posun vpravo, budou dvě, pojmenované podle našeho předchozího pravidla:

posunVpravo posunVpravo#int

Všimněte si, že v tomto interním jméně není ani zmínka o typu návratové hodno-ty. Proto nemůžeme definovat metody, které by se lišily pouze v typu návratové hodnoty, protože by pak měly pro překladač stejná jména a to překladač nepři-pustí (můžete si to vyzkoušet).

Page 128: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

128 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 128 z 433

Při zápisu zpráv jsem doposud uváděl názvy parametrů tak, jak je BlueJuváděl v místní nabídce třídy nebo instance. Názvy konstruktorů a me-tod však již nebudu uvádět se seznamem názvů parametrů, ale se se-znamem jejich typů, protože právě podle nich překladač jednotlivéverze přetížené zprávy rozlišuje.

3.13 Zapouzdření V průběhu dosavadního výkladu jsem se několikrát zmiňoval o tom, že by okolní třídy neměly o tom či onom vědět. Definovali jsme soukromé atributy a hovořili jsme i o možnosti definovat soukromé metody a konstruktory. Proč to všechno schovávání? Jednou ze základních a velice ceněných vlastností objektově oriento-vaných programů je schopnost tzv. zapouzdření. Lidově bychom mohli zapouz-dření charakterizovat heslem:

„Nikdo nesmí mít šanci zjistit, jak to dělám, že umím to, co umím.“

Takto osamoceně vyslovena vypadá možná tato zásada neurvale, ale věřte, že je to nejvíce ceněná vlastnost celého OOP. Čím jsou programy složitější, tím je důle-žitější, abychom ani omylem nemohli ovlivnit chod některé jiné části.

V naši analogii bychom mohli říci, že pro vozidlo-instanci je mnohem výhodnější, má-li své šuplíky uvnitř, kde o nich ví pouze jeho osádka,než aby je mělo rozmístěné zvenku vozidla, kde může kdokoliv jejich obsah vyměnit, aniž by se o tom posádka dozvěděla.

Rozhraní × implementace V této souvislosti se seznámíme se dvěma novými termíny:

Rozhraní třídy budeme chápat jako množinu informací, které o sobě třída zveřejní. Mezi rozhraní patří např. vše, co třída označí modifikátorem public.

Implementace je způsob, jak je třída naprogramována, tj. jak to dělá, že umí to, co umí.

Do rozhraní bychom měli zařadit pouze to, co ostatní části programu o dané třídě opravdu musí vědět. Když jsme například chtěli, aby ostatní programy mohly na-ši třídu požádat o vytvoření instance, museli jsme zveřejnit její konstruktor. Bu-

Page 129: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 129

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 129 z 433

deme-li chtít, aby ostatní programy mohly zjistit, kde je právě daný strom nakres-len, musíme zveřejnit metody, pomocí nichž mohou tuto informaci získat.

Vše, co sice k implementaci požadovaných funkcí potřebuji, ale o čem se do-mnívám, že ostatní vědět nemusí, označím jako private. Nechci-li, aby ostatní čás-ti programu mohly pohybovat jednotlivými částmi stromu bez mého vědomí, nesmím je k nim pustit – označím proto příslušné atributy jako private.

Mezi public a private existují ještě mezistupně, ale o těch si povíme až si s objektovým programováním trochu více potykáte.

Do rozhraní se počítají i informace, které z hlaviček nevyčtete, ale které by měly být uvedeny v dokumentaci. Sem patří informace o dalších podmínkách, které je třeba dodržet (např. že zadávané souřadnice vy-tvářeného tvaru musí být větší než 0, že instance metody Plátno je jedi-náček apod.), o možných vedlejších efektech funkcí (např. co se stane, když obrazec „vycestuje“ z plátna) a řada dalších důležitých sdělení. Tento souhrn informací bývá označován jako kontrakt a musí být uve-den v dokumentaci třídy.

Jak jsem již řekl, prakticky každý program jeho tvůrci v průběhu doby upravují. Jakmile však třída zveřejní některé své vlastnosti a schopnosti a ostatní třídy za-čnou její služby používat, nemohou s tím, co o sobě třída zveřejnila, její tvůrci v pozdější době již nic dělat (lépe řečeno neměli by), protože by tím ohrozili chod všech programů, které tyto vlastnosti využívají.

Na druhou stranu platí: dokud nezměním rozhraní, mohu si s programem dělat, co chci. Jakékoliv vylepšení či zdokonalení, které se nepromítne do dekla-rovaného rozhraní, můžeme realizovat s čistým svědomím, že tím funkčnost spo-lupracujících programů neovlivníme.

Přístupové metody Jak jsme si řekli na začátku části o atributech, bývá dobrým zvykem deklarovat všechny atributy jako soukromé. Budeme-li chtít uživatelům naší třídy umožnit zjišťovat či měnit hodnoty některého z atributů, definujeme k tomu zvláštní pří-stupové metody, které navíc dělíme na čtecí a nastavovací. Toto řešení má několik výhod:

Můžeme zařídit, že hodnoty některých atributů bude možno pouze číst, avšak nikoliv nastavovat.

Page 130: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

130 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 130 z 433

Dovolíme-li nastavování nových hodnot atributů, tj. definujeme-li příslušné metody setXxx, můžeme v nich ohlídat, aby uživatel nenastavoval atributům nějaké nesmyslné hodnoty.

Při nastavování hodnot atributů můžeme provést ještě potřebné doprovodné akce (např. při změně pozice by se měla instance ve staré pozici smazat a na-kresli se v pozici nové).

Můžeme dát příslušné metody k dispozici nezávisle na tom, jestli třída do-tyčné atributy opravdu má nebo jestli jsou tyto atributy jenom fiktivní. Zven-ku třídy, tj. při volání jejích přístupových metod, nemáme šanci zjistit, jestli třída uvnitř pracuje se skutečnými a nebo s vypočítanými atributy.

Fiktivní atributy by pro vás měly být něčím důvěrně známým, protože s nimi u našich stromů již dávno pracujeme. Atributy x, y, šířka, výškajsou typickými příklady takovýchto fiktivních atributů. Instance sice ty-to atributy nemá, ale tváří se, jako že je má. Když se pak někdo stromu zeptá na jeho souřadnice nebo rozměr, strom se do žádného atributunedívá (kam by se také díval, když jej nemá) ale zjistí si potřebnou in-formaci jinak a zjištěnou informaci vrátí.

Obdobně je to i s nastavováním těchto atributů. Nastavíte-li třeba novou pozici, strom si nic nikam neukládá, ale pouze zařídí, aby se do požadované pozice přesunul.

Když si později vzpomeneme, že fiktivní atributy nahradíme skutečnými (např. proto, že jejich výpočet je příliš pomalý) nebo naopak nahradíme sku-tečné atributy fiktivními (např. proto, že jejich zjištění či nastavení je tak jed-noduché, že nemá smysl si je pamatovat), uživatel se nic nedozví, protože se na atributy bude stále obracet prostřednictvím jejich přístupových metod.

Nabídne-li třída v naší analogii nějaké přístupové metody, tj. vybaví-li vozidlo robotem, který vám umí odpovědět na otázku po hodnotě atri-butu a/nebo robotem, který umí atribut nastavit, je to jako byste měli možnost do našeho robotího vozidla zavolat a na hodnoty v příslušnémšuplíku se pověřeného robota-metody zeptat nebo požádat o její změ-nu. Vy přitom nemáte šanci zjistit, jestli se robot opravdu podíval na hodnotu do šuplíku, anebo jestli ji na poslední chvíli získal nějakou al-ternativní metodou (třeba si hodil kostkou).

Page 131: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 131

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 131 z 433

Hlídání hodnot začneme používat, až budeme umět naprogramovat nějaké roz-hodování. S fiktivními atributy jsme ale již pracovali. Vždyť naše stromy nemají atributy, v nichž by měly uloženu svoji pozici nebo rozměr. Nepotřebují je, proto-že si tyto hodnoty umějí kdykoliv snadno zjistit a stejně snadno je umějí na požá-dání „nastavit“.

Ve třídě Strom máme prozatím definované přístupové metody pouze k fiktiv-ním atributům x, y, šířka, výška a dejme tomu ještě barvaKoruny a barvaKmene. Na-víc jsme definovali nastavovací metody pouze k prvním dvěma atributům a u všech ostatních atributů jsme prozatím definovali pouze metody čtecí. Ke skuteč-ným atributům však v této třídě nikoho nepouštíme.

Třídy Elipsa, Obdélník a Trojúhelník nabízejí zase čtecí metodu ke svému atributu název, avšak nenabízejí již k němu zapisovací metodu, takže uživatel ne-má šanci tento název změnit. Název získaný při svém „narození“ si tak instance podrží až do své „smrti“.

Možná, že vás zarazilo, proč jsem pro přístupové metody volil takové divné názvy. Odpověď je jednoduchá: chtěl jsem dodržet konvence pro názvy těchto metod.

Konvence pro názvy přístupových metod Pro názvy přístupových metod platí zvláštní konvence, které je vhodné dodržo-vat, protože jsou na ně zvyklí nejenom programátoři, ale je na nich postavena i funkčnost některých programů a technologií:

Názvy metod sestávají z předpony následované názvem příslušného atribu-tu, přičemž je jedno, jestli je daný atribut skutečný nebo fiktivní. Za předpo-nou se první písmeno tohoto názvu píše velké, i když je ve skutečnosti (alespoň dle konvencí) malé.

Nastavovací metody (tj. metody, které nastavují hodnotu příslušného atribu-tu) používají předponu set.

Čtecí metody (tj. metody, které vracejí hodnotu příslušného atributu) použí-vají příponu get. V případě, že vracejí logickou hodnotu (tj. hodnotu typu ANO/NE, přesněji true/false), mohou mít předponu is.

Podle těchto předpon označují programátoři často nastavovací metody jako „setry“ a čtecí metody jako „getry“. Já se však v této učebnici budu držet českých termínů.

Page 132: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

132 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 132 z 433

Kontrakt Rozhraní není definováno pouze hlavičkami veřejných metod. Ty mohou defino-vat jen syntaktická pravidla, která bude překladač kontrolovat při překladu. Vedle těchto syntaktických pravidel ale do rozhraní patří i tzv. kontrakt (mohli bychom jej nazvat dohodou mezi tvůrcem třídy či metody a jejím uživatelem).

Do kontraktu zařazujeme další podmínky, které je nutno při práci s danou třídou resp. při volání další metody splnit, avšak které nemůžeme specifikovat prostředky jazyka, jako to děláme např. s požadovaným typem parametrů. Mů-žeme např. vyžadovat, aby číselný parametr byl nezáporný nebo dokonce kladný, aby řetězcový parametr obsahoval minimální či maximální zadaný počet znaků nebo aby splňoval nějakou jinou podmínku.

Tyto podmínky nemusí být kladeny pouze na konkrétní parametry, ale např. i na vzájemné vztahy mezi jednotlivými parametry, resp. mezi parametry a okol-ním světem.

Všechny záležitosti týkající se kontraktu (tj. co se od třídy, resp. metody oče-kává), by měly být uvedeny v dokumentaci třídy, resp. metody. Jinak se totiž o nich uživatel nemá šanci dozvědět a mohl by je používat špatně.

Např. v našem příkladu s geometrickými tvary patří do kontraktu informace o tom, že konstruktor vyžaduje pouze kladné hodnoty souřadnic a rozměrů, co že jsou to vlastně souřadnice obrazce (vzdálenost levého horního rohu opsaného ob-délníku od levého horního rohu plátna), že plátno je jedináček, co přesně zname-nají při konstrukci trojúhelníku zadané směry, atd., atd.

Dodržení kontraktu sice překladač kontrolovat nedokáže, nicméně počítejte s tím, že jeho nedodržení vede často k havárii programu nebo k jeho podivnému, nepředvídatelnému chování.

3.14 Kvalifikace a klíčové slovo this Název proměnné nebo třídy následovaný tečkou je označován jako kvalifikace. Kvalifikace vlastně určuje, komu že chceme danou zprávu poslat.

Kvalifikace metod Každou metodu je třeba před jejím zavoláním kvalifikovat, protože musíme pře-kladači sdělit, čí metodu voláme, tj. komu že posíláme příslušnou zprávu.

Pro kvalifikaci platí následující pravidla:

Posíláme-li zprávu třídě, musíme volání metody kvalifikovat názvem třídy.

Page 133: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 133

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 133 z 433

Posíláme-li zprávu instanci, musíme volání metody kvalifikovat odkazem na tuto instanci.

Předchozí pravidlo má jedinou výjimku: voláme-li v metodě instance jinou metodu té samé instance, toto volání kvalifikovat nemusíme. Překladač totiž ví, že volat nekvalifikovanou metodu instance je nesmysl, takže pokud tak učiním, tj. pokud v programu zavolám nekvalifikovanou metodu instance, pokusí se ji kvalifikovat tou instancí, jejíž metodu právě překládá.

Pokud bychom ale chtěli v metodě instance kvalifikovat i metody této instan-ce, můžeme pro jejich kvalifikaci použít klíčové slovo this. Toto klíčové slovo zastupuje odkaz na instanci, v jejíž metodě se nacházíme.

Metodu třídy můžeme teoreticky kvalifikovat i odkazem na kteroukoliv z jejích instancí, ale takováto kvalifikace není považována za programá-torsky čistou, protože může při analýze programu vyvolat dojem, že sejedná o metodu instance. Proto tento způsob kvalifikace nebudu použí-vat.

Situaci, kdy si překladač kvalifikaci domyslí, označujeme implicitní kvalifikace, na rozdíl od explicitní kvalifikace, při níž programátor kvalifikuje metodu sám.

Nyní jsme teoreticky vyzbrojeni, takže můžeme hned zkusit nabyté vědomosti použít. Vše si ukážeme na následujícím příkladu:

Příklad: Definujte metodu void zarámuj(), která svoji instanci na plátně zarámuje, tj. upraví rozměry plátna tak, aby na něm byla pouze její instance.

Rozmysleme si nejprve, jak bychom mohli postupovat:

Velikosti plátna změníme zavoláním jeho metody setRozměr(int,int).

Abychom mohli zavolat metodu plátna, musíme mít nejprve odkaz na toto plátno. Získáme jej zavoláním metody getPlátno() třídy Plátno.

Odkaz na instanci, který takto získáme, nemusíme odkládat do žádného atributu, ale můžeme jej přímo použít k tomu, abychom zavolali metodu na-stavující rozměr (otazníky označují zatím neznámé části programu): Plátno.getPlátno().setRozměr( ???, ??? );

Page 134: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

134 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 134 z 433

Velikost plátna má být stejná jako velikost příslušné instance. Tu zjistíme tak, že zavoláme její metody getŠířka() a getVýška(). Obdržené výsledky opět nemusíme nikam ukládat, ale můžeme je hned předat jako parametry metodě pro nastavení velikosti plátna. Příkaz tedy bude vypadat: Plátno.getPlátno().setRozměr( getŠířka(), getVýška() );

Po změně rozměru se plátno smaže. To nám ale nevadí, protože stejně musí-me přesunout naši instanci do levého horního rohu plátna.

Po předchozích úvahách je již asi jasné, jak by mohla definice metody zarámuj() vypadat: 1 2 3 4 5

public void zarámuj() { Plátno.getPlátno().setRozměr( getŠířka(), getVýška() ); setPozice( 0, 0 ); }

Pokud bychom chtěli kvalifikovat i metody vlastní instance, získal by kód tvar: 1 2 3 4 5

public void zarámuj() { Plátno.getPlátno().setRozměr( this.getŠířka(), this.getVýška() ); this.setPozice( 0, 0 ); }

Zkuste sami navrhnout test této metody. Můžete jej definovat např. tak, že po načtení přípravku do zásobníku požádáte o zarámování první in-stanci a pomocí metody P.souhlas se zeptáte, jestli je výsledek správný.Pak můžete zobrazenou instanci smazat a požádat o zobrazení další in-stance a opět se zeptat na správnost výsledku. Tentokrát by se mělo ok-no přizpůsobit velikosti nově rámované instance.

Kvalifikace atributů Totéž, co jsme si před chvílí říkali o metodách, platí i o atributech. Ty však bývají většinou soukromé, takže o ně „cizí“ žádat nemohou a „domácí“ je kvalifikovat nemusejí. Proto se s jejich kvalifikací příliš často nepotkáte. Někteří programátoři však kvalifikují vše, takže pak mají „domácí“ atributy kvalifikované klíčovým slovem this.

Page 135: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 135

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 135 z 433

V jednom případě je ale kvalifikace klíčovým slovem this nutná: shoduje-li se název parametru s názvem atributu a potřebuji-li v dané metodě pracovat s oběma (většinou potřebuji parametru přiřadit hodnotu atributu). Tato situace bý-vá relativně častá, protože programátoři bývají líní vymýšlet nové názvy a navíc bývá takovýto název parametru nejnázornější.

Program pracuje podle zásady „bližší košile než kabát“. Když se protov metodě objeví parametr, který se jmenuje stejně jako atribut, metoda přestane na stejně pojmenovaný atribut vidět. Jedinou možností jak jej„zviditelnit“ je „přejmenovat jej“ pomocí přidaného this.

Ukažme si to na příkladu třídy Pozice, jejíž instance budou mít za úkol si zapama-tovat vodorovnou a svislou souřadnici zadané pozice. Tuto třídu bychom mohli definovat např. následovně: 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 30 31 32 33 34

public class Pozice { private int x; private int y; public Pozice( int x, int y ) { this.x = x; this.y = y; } public int getX() { //Tady se x s ničím nehádá, takže this používat nemusíme return x; //ale můžeme, tj. lze napsat: return this.x; } public int getY() { return y; } public void setPozice( int x, int y ) { this.x = x; this.y = y; } public void setPozice( Pozice pozice ) { this.x = pozice.x; this.y = pozice.y; } }

Page 136: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

136 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 136 z 433

Někteří žáci se podivují poslední metodě. Ptají se: ʺProč mám nastavo-vat pozici, když ji už mám?ʺ

Poslední metodu použijete tehdy, máte-li jednu instanci třídy Pozicea potřebujete-li, aby druhá instance ukazovala na tu samou pozici. Pře-dáte proto metodě první instanci jako parametr a metoda zkopíruje hodnoty jejích atributů do atributů druhé instance.

Už jsem po vás dlouho nic nechtěl ☺. Zkuste definovat třídu Rozměr, je-jíž instance by si pro změnu pamatovali zadanou šířku a výšku.

3.15 Atributy a metody třídy (statické atributy a metody)

Když jsme si v minulé kapitole hráli s geometrickými obrazci, ukazovali jsme si, že každý z nich poskytuje metody, které mohou pohnout se zadaným obrazcem o předem daný počet bodů. Tento počet byl uložen v atributu třídy nazvaném krok. Jeho hodnotu bylo možno nastavit metodou setKrok().

Tato možnost ale představuje pro náš strom značné nebezpečí. Zkuste požá-dat např. třídu Elipsa, aby zmenšila velikost svého kroku na 25, a pak znovu spusťte test Posuny. Jeho průběh vás určitě nepotěší.

Atributy třídy Jednou z možností, jak chování naší třídy vylepšit, je zavést pro ni její vlastní atri-but krok a ten pak používat při posunu stromů pomocí bezparametrických metod.

Atributy třídy definujeme stejně jako atributy instancí, pouze mezi jejich mo-difikátory přidáme klíčové slovo static. Na pořadí modifikátorů přitom nezáleží, ale doporučuji vám, abyste si na nějaké zvykli. Já ve svých programech např. uvádím modifikátor přístupu vždy jako první a modifikátor static až za ním.

Podle modifikátoru static bývají atributy a metody třídy označovány často jako statické na rozdíl od atributů a metod instancí, které bývajíobčas označovány jako nestatické.

Page 137: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 137

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 137 z 433

Po zavedení statického atributu krok můžeme všechny bezparametrické posunové metody upravit tak, aby posunuly strom o tolik bodů, kolik je hodnota tohoto atributu. Část kódu, která s touto úpravou souvisí, by mohla vypadat např. násle-dovně: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21

private static int krok = 50; public void posunVpravo() { posunVpravo( krok ); } public void posunVlevo() { posunVpravo( -krok ); } public void posunVzhůru() { posunDolů( krok ); } public void posunDolů() { posunDolů( -krok ); }

Na rozdíl od atributů instancí, které můžeme inicializovat buď přímo v deklaraci nebo později v konstruktoru, pro inicializaci atributů třídy prozatím žádný kon-struktor neznáme (existuje, ale do začátečnických kapitol přeci jenom nepatří). Zbývá nám tedy prozatím pouze inicializace v deklaraci. Naštěstí to však není žádné velké omezení, protože inicializovat můžeme i voláním nějaké metody, kte-rá potřebnou hodnotu nejprve spočítá.

Metody třídy Stejně jako atributy třídy existují i metody třídy a i ony se definují vložením klíčo-vého slova static mezi modifikátory. Jejich výhodou je, že je můžeme zavolat ješ-tě před tím, než vznikne její první instance. Slouží proto často k přípravě prostředí, ve kterém následně instance vznikne, případně k definici metod, které nejsou na žádnou instanci vázány (takto je např. definována většina matematic-kých funkcí).

Jako metody třídy je nutné definovat např. metody, které zjišťují nebo nasta-vují hodnoty atributů třídy (i ty můžeme např. číst a nastavovat ještě před tím, než vznikne první instance dané třídy).

Page 138: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

138 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 138 z 433

Před chvilkou jsme si vysvětlovali, že atributy třídy můžeme inicializo-vat i prostřednictvím volání metod. Protože se tyto atributy inicializují ještě před tím, než se třída poprvé použije, musíme k jejich inicializacipoužít statických metod, protože jedině ty můžeme volat ještě před tím, než vznikne jakákoliv instance.

Předpokládám, že nyní byste dokázali bez problému definovat metody pro zjiště-ní a nastavení hodnoty atributu krok.

Abych vám rozšířil obzory, tak k těm dvěma základním přidám ještě bezpa-rametrickou verzi metody setKrok(), která o zadání velikosti kroku požádá uživa-tele. Využije k tomu statickou metodu zadej(Object,int) třídy P, které předá jako první parametr text výzvy k zadání hodnoty (ten má deklarován typ Object, takže sem můžete zadat cokoliv). Jako druhý parametr ji předá implicitní hodnotu, kte-rou bude v našem případě aktuální hodnota kroku. 1 2 3 4 5 6 7 8 9

10 11 12 13 14

public static int getKrok() { return krok; } public static void setKrok( int krok ) { Strom.krok = krok; } public static void setKrok() { krok = P.zadej( "Zadejte novou velikost kroku:", krok ); }

Jak jsem řekl, metody třídy nezávisejí na žádné instanci a můžeme jevolat ještě před tím, než první instance vznikne. Je proto logické, žev metodách třídy nemůžeme používat atributy a metody instancí, přesněji řečeno atributy a metody instancí kvalifikované implicitně ne-bo explicitně klíčovým slovem this. Překladač totiž nemá žádnou in-formaci o tom, která instance by se v daném okamžiku mohla za thisskrývat a čí metodu má proto vyvolat (komu má danou zprávu poslat).Vytvoříme-li si však v metodě třídy vlastní instanci, tak její metody sa-mozřejmě používat můžeme.

Page 139: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 139

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 139 z 433

1. Naprogramujte podobné rozšíření i pro vaši třídu.

2. Definujte statickou metodu zarámuj(int,int), které byste dodali výšku a šířku požadovaného stromu jako parametry, a ona by da-ný strom vytvořila a upravila podle něj velikost plátna.

3. Definujte statickou metodu alej(), která vytvoří alej ze dvou řad stromů po třech. Můžete si vybrat, jestli bude alej orientovaná vo-dorovně, svisle nebo šikmo.

4. Definujte potřebné testy.

Protože druhý a třetí úkol již od vás vyžaduje trochu přemýšlení, neboť není je-nom jednoduchou variací toho, co jsme už naprogramovali, přidám pro ty, kte-rým se vlastní řešení nedaří, ukázku toho, jak by mohlo řešení vypadat. Testy si snad dokážete definovat sami. 1 2 3 4 5 6 7 8 9

10 11 12 13 14

public static void zarámuj( int šířka, int výška ) { Plátno.getPlátno().setRozměr( šířka, výška ); new Strom( 0, 0, šířka, výška ); } public static void alej() { Plátno.getPlátno().setRozměr( 400, 350 ); new Strom( 100, 0 ); new Strom( 300, 0 ); new Strom( 50, 100 ); new Strom( 250, 100 ); new Strom( 0, 200 ); new Strom( 200, 200 ); }

3.16 Lokální proměnné Často se stává, že si v metodě potřebujeme na chvilku něco zapamatovat. V tako-vém okamžiku by se nám hodilo něco jako „atribut metody“. Taková věc opravdu existuje a říká se jí lokální proměnná. Lokální proto, že o ní ví pouze její bezpro-střední okolí (v našem případě kód uvnitř metody) a proměnná proto, že hodnotu, kterou si do ní uložíme, můžeme za chvilku změnit.

Lokální proměnné se deklarují téměř stejně jako atributy. Jejich deklarace a použití se liší pouze v několika drobnostech:

Deklarují se uvnitř metod, tj. v jejich těle.

Page 140: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

140 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 140 z 433

V jejich deklaraci nesmíme použít modifikátory přístupu ani modifikátor static.

Mimo jejich metodu o nich nikdo neví. Bude-li proto jiná metoda definovat stejně pojmenované lokální proměnné, budou možná stejně pojmenované, ale budou to naprosto jiné proměnné.

Je to obdobné, jako když budete mít doma morče pojmenované Ferda(programátorsky: budete mít lokální proměnnou Ferda typu Morče) a váš kamarád na druhém konci města bude mít stejně pojmenovanémorče. Obě jsou to morčata, obě mají stejné jméno, ale nikdo nepředpo-kládá, že nakrmíte-li vašeho Ferdu, přestane mít kamarádův Ferdahlad. Stejně pohlížejte i na lokální proměnné metod.

Podíváme-li se na naší „oblíbenou analogii“ s roboty, mohli bychomsi lokální proměnné představit jako kapsy, do nichž si robot ukládá po-třebné informace. Do kapes robotovi nikdo nevidí. Představují tak třetí,nejsoukromější vrstvu uložených informací.

Před jejich prvním použitím jim musíte přiřadit nějakou počáteční hodnotu. Neučiníte-li tak, ohlásí překladač chybu, protože odmítá vytvořit program, který by pracoval s nějakým smetím, jež by se zrovna nacházelo v paměti na místě, které by pro danou proměnnou vyhradil.

Jakmile metodu opustíte, proměnná se zruší a při příštím spuštění metody se znovu vytvoří. Není proto možné uchovávat v lokálních proměnných coko-liv, co si potřebujeme pamatovat mezi jednotlivými voláními dané metody. K tomu musíte použít atributy.

Podíváte-li se na vlastnosti lokálních proměnných, zjistíte, že parametry jsou vlastně zvláštním případem lokálních proměnných. Jsou to lokální proměnné, jejichž deklarace metoda vysunula do své hlavičky, aby onich okolní program věděl a mohl nastavit jejich počáteční hodnoty. Pa-rametry se od ostatních lokálních proměnných liší opravdu pouze tím, že jejich počáteční hodnoty nenastavuje metoda, ale program, který tuto metodu volá. Pak už k nim ale volající program ztratí přístup a metodasi s nimi může dělat, co uzná za vhodné, aniž by to mohl volající pro-gram jakkoliv ovlivnit.

Takže nyní už všechno víme a můžeme začít programovat. Vrátíme se k definici konstruktoru se čtyřmi parametry (viz program na straně 103, řádek 13). V tomto

Page 141: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 141

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 141 z 433

konstruktoru jsme několikrát použili výraz výška/3. Kolikrát je použit, tolikrát se musí spočítat.

Mohli bychom proto program upravit tak, že bychom si definovali pomocnou lokální proměnnou, kterou bychom nazvali např. v3. Do ní bychom uložili výsle-dek výrazu výška/3 a pak bychom ji dosadili místo tohoto výrazu. Upravený pro-gram by vypadal následovně: 1 2 3 4 5 6 7

public Strom( int x, int y, int šířka, int výška ) { int v3 = výška / 3; koruna = new Elipsa ( x, y, šířka, 2*v3, Barva.ZELENÁ ); kmen = new Obdélník( x+9*šířka/20, y+2*v3, šířka/10, v3, Barva.ČERVENÁ ); }

Program se po úpravě teoreticky zrychlí, protože se výraz již nebude počítat tři-krát, ale pouze jednou. Zrychlení ale není nejčastějším důvodem, proč zavádíme lokální proměnné (navíc je v tomto případě prakticky neměřitelné). Dalším (a čas-to důležitějším) důvodem bývá zpřehlednění programu a v neposlední řadě i sní-žení počtu chyb zavlečených v důsledku opakovaného opisování složitých výrazů.

Je tu ale ještě jeden důvod. Dobrý programátor se snaží nikdy nepsat stejný kód dvakrát. Nejde jen o snížení pravděpodobnosti vzniku chyby, ale také o to, že se tím výrazně zjednoduší případná pozdější modifikace programu.

Vezměme si např. situaci, kdy bychom se rozhodli změnit proporce stromu a definovat výšku kmene ne jako třetinu celkové výšky stromu, ale např. jako jeho polovinu nebo čtvrtinu. V původním programu bychom museli vyhledat všechny výskyty výrazu výška/3, nahradit je novým výrazem a věřit, že jsme žádný z nich nepřehlédli. V upraveném programu je to mnohem jednodušší: stačí změnit vý-raz, kterým přiřazujeme počáteční hodnotu proměnné v3.

Upravte program tak, aby se do proměnné v3 ukládala pouze polovina požadované výšky stromu, a prožeňte jej testy.

Tak co, vyzkoušeli jste to? Přišli jste na chybu? Předpokládám, že ano: chyba byla v tom, že jsme sice do proměnné v3 vložili budoucí výšku kmene, ale neupravili jsme podle toho výraz, který vypočítává výšku koruny a v dalším příkazu výraz, který vypočítává posunutí svislé souřadnice kmene vůči svislé souřadnici celého stromu. Celý strom je proto vyšší, než jsme požadovali (viz obr. 3.28).

Page 142: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

142 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 142 z 433

Obrázek 3.28 Vlevo obrázek „třetinového“ stromu, vpravo obrázek „polovičního“ stromu.

Oba stromy mají nastavenu výšku 150.

Lidově řečeno odflákli jsme to (ještě že máme k dispozici ty automatické tes-ty). Podívejme se proto, jak bychom měli program upravit, aby v něm bylo možno snadno změnit poměr výšky kmene k velikosti stromu. A když už o takové mož-nosti uvažujeme, definujeme rovnou nový konstruktor, v němž umožníme zadat jako parametry podíl výšky a šířky kmene na výšce a šířce stromu tj. kolikrát je ce-lý strom širší, resp. vyšší než samotný kmen. 1 2 3 4 5 6 7 8 9

10 11

public Strom( int x, int y, int šířka, int výška, int podílVýškyKmene, int podílŠířkyKmene ) { int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene; int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = (šířka – šířkaKmene) / 2; koruna = new Elipsa ( x, y, šířka, výškaKoruny, Barva.ZELENÁ ); kmen = new Obdélník( x+posunKmene, y+výškaKoruny, šířkaKmene, výškaKmene, Barva.ČERVENÁ ); }

Nový konstruktor si hned vyzkoušejte. Při tvorbě testovací metody vám doporu-čuji nejprve smazat a poté i odstranit ze zásobníku odkazů všechny instance z přípravku a pak při vytváření nových instancí použít jména strom1 až strom4. Za chvilku si ukážeme, čeho tím dosáhnete.

Page 143: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 143

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 143 z 433

Vyzkoušejte si použití lokálních proměnných ve vašich vlastních tří-dách. Zkuste v nich také definovat nějaký univerzálnější konstruktor obdobný šestiparametrovému konstruktoru třídy Strom.

Když teď máme definovaný univerzální konstruktor, měli bychom příslušně upravit i testovací třídu. Jednou z možností je definovat nový přípravek, který bude obsahovat i stromy, při jejichž konstrukci jsme použili všech šest parametrů.

3.17 Konstanty a literály V programech často používáme nejrůznější „magické hodnoty“, které se v průbě-hu programu nemění. Budeme-li chtít např. pracovat s počtem dnů v týdnu, bu-deme neustále používat číslo 7. Problém nastane, když se při některé z pozdějších úprav rozhodneme, že místo počtu dnů v celém týdnu bude pro náš program vý-hodnější používat pouze počet pracovních dnů, tj. 5 (alespoň prozatím to tak je). Taková úprava pak znamená prolézt celý program a všechny sedmičky nahradit pětkami.

Již samotná tato představa je dostatečně nepříjemná. Noční můrou se ale sta-ne, pokud je program opravdu rozsáhlý a navíc je v něm řada různých „sedmi-ček“ – některé sedmičky budou např. znamenat, že v červenci začínají prázdniny a další sedmičky budou oznamovat, že pracujeme od 7 hodin ráno.

Rozumní programátoři proto takovéto „magické hodnoty“ nepoužívají a dá-vají přednost pojmenovaným konstantám. Ty se v Javě definují stejně jako atribu-ty, pouze se mezi jejich modifikátory uvede klíčové slovo final.

Jako konstanty je možné deklarovat i parametry a lokální proměnné – opět toho dosáhneme tak, že jejich deklaraci uvodíme klíčovým slovem final.

Výše popsané „magické hodnoty“ označujeme jako literály. Literál je konstanta zapsaná svoji hodnotou – např. 7 (celočíselný literál), ʺAhojʺ (řetězcový literál) apod. Naproti tomu konstanty, o nichž jsme právěhovořili a které deklarujeme podobně jako proměnné, označujeme jakopojmenované konstanty, protože ve své definici dostanou jméno, kte-rým se na ně v programu odvoláváme.

Budu-li v dalším textu hovořit o konstantách, bud tím vždy mysletpojmenované konstanty. Pokud bych chtěl hovořit o literálech, vždy to výslovně uvedu.

Page 144: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

144 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 144 z 433

Názvy konstant, které jsou atributy třídy (tj. názvy statických konstant), se podle konvencí píší velkými písmeny s tím, že se jednotlivá slovanázvu oddělují znaky podtržení. U názvu ostatních konstant se již tato konvence tak přísně nedodržuje.

Konstantám můžeme přiřadit hodnotu pouze jednou a již nikdy ji nemůžeme změnit:

Statickým konstantám, tj. konstantám, které jsou atributy třídy, přiřadíme je-jich hodnotu hned v deklaraci.

Nestatickým konstantám, které nemají svoji hodnotu přiřazenou v deklaraci, musíme přiřadit hodnotu v konstruktoru. Neučiníme-li tak, překladač to označí jako chybu.

Konstantním parametrům se přiřadí hodnota při volání příslušné metody.

Lokálním konstantám je třeba přiřadit hodnotu hned v jejich deklaraci.

Poznámka o dobrých mravech: Z číselných literálů se doporučuje používat pouze hodnoty 0 a 1. Ostat-ní hodnoty je lépe definovat jako pojmenované konstanty. Dokonce inuly a jedničky se mají používat pouze v situacích, kdy tyto hodnotyneoznačují nic, co by se mohlo v některé z příštích verzí programuzměnit – např. v současné době mám jen jedno kolo (auto, peněženku,manželku, …), ale dokážu si představit situaci, kdy se tato jedničkazmění.

Obdobně i řetězcové literály je vhodné používat pouze v situacích,kdy se daný řetězec použije pouze jednou (např. v chybovém hlášení). Chcete-li však program lokalizovat do více jazyků, je vhodné i jednoupoužité textové řetězce pojmenovat a definovat všechny na jednom místě, kde je lze snadno nahradit jejich překlady.

Konstantní parametry se příliš nepoužívají, i když většina metod jejichhodnotu ve svém těle nemění. Používají se pouze v některých situacích, kdy definice jazyka použití konstanty vyžaduje. O těchto případech si podrobněji povíme, až budeme probírat náročnější pasáže.

Doplňme nyní definici třídy strom o konstanty IMPLICITNÍ_POMĚR_VÝŠKY a IMPLICITNÍ_POMĚR_ŠÍŘKY které budou obsahovat výchozí hodnoty 2 a 10, s nimiž

Page 145: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 145

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 145 z 433

jsme stromy začali vytvářet. Upravme pak čtyřparametrický konstruktor tak, aby pouze zavolal konstruktor šestiparametrický. 1 2 3 4 5 6 7 8 9

public static final int IMPLICITNÍ_POMĚR_ŠÍŘKY = 2 public static final int IMPLICITNÍ_POMĚR_VÝŠKY = 10; public Strom( int x, int y, int šířka, int výška ) { this( x, y, šířka, výška, IMPLICITNÍ_POMĚR_ŠÍŘKY, IMPLICITNÍ_POMĚR_VÝŠKY ); }

Všimněte si, že jsem v ukázce definoval konstanty jako veřejné (public). Konstan-ty primitivních datových typů totiž nemůže nikdo změnit, a proto se konstanty, které by se někomu mohly hodit, většinou deklarují jako veřejné. Pokud si však nemyslíte, že by bylo užitečné, aby někdo hodnotu takové konstanty znal, po-nechte je raději dále jako soukromou.

Konstanty objektových typů Konstanty nemusí být pouze hodnotami primitivních datových typů. Připomeň-me si však, že hodnotou atributu objektového typu je odkaz na příslušnou instan-ci. Deklarujeme-li tedy atribut objektového typu jako konstantní, překladač zabezpečí, abychom nezměnili tento odkaz. Co se však děje s vlastní instancí jej nezajímá.

Vzpomeňte si na náš strom a jeho atributy koruna a kmen. Na počátku jsme jim v konstruktoru přiřadili počáteční hodnotu (vytvořili jsme příslušnou instanci a do atributu uložili odkaz na ni) a pak už jsme všude pracovali stále s těmi samými objekty. Mohli jsme je přesouvat, mohli jsme měnit jejich velikost či barvu, ale stá-le to byla ta samá elipsa či obdélník, které jsme vytvořili při konstrukci stromu.

Pokud bychom chtěli tuto neměnnost instancí nějak zabezpečit, mohli by-chom korunu a kmen deklarovat jako konstanty instance, a překladač by pak do-hlédl na to, abychom je v žádné metodě nezaměnili za jiné. Nijak by však nesledoval, co s těmito instancemi provádíme.

Zveřejňování konstant objektových datových typů, tj. deklarace těchto kon-stant jako veřejných atributů, je trochu složitější než u konstant s hodnotami pri-mitivních typů. Teď to zatím nebudu rozebírat a vrátím se k této otázce později v kapitole Hodnotové a referenční typy na straně 222.

Page 146: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

146 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 146 z 433

Správná podoba literálů Nadpis tohoto oddílu možná některé z vás zarazil. Proč si máme povídat o správ-né podobě literálů, když jsem je před chvílí tak pomluvil. Důvod je jednoduchý: bez literálů se programovat nedá. Použití pojmenovaných konstant je sice mno-hem výhodnější, jenže těmto konstantám musíme jednou přiřadit počáteční hod-notu, a to bez literálů nejde.

Probereme si nyní postupně literály jednotlivých primitivních typů. Přehled zakončíme řetězcovými literály.

boolean S logickými literály to je nejjednodušší, protože mohou nabývat pouze dvou hod-not: true označuje pravdivou hodnotu a false hodnotu nepravdivou.

int Ani s celočíselnými literály to nebude složité. Celá čísla jste jistě všichni znali a uměli psát ještě dříve, než jste začali chodit do školy. V programu se píší praktic-ky stejně, jako jste zvyklí z obyčejného života. Několik odchylek by se ale našlo:

V programu nesmíte zapsat uvnitř celého čísla mezeru – to by byla chyba. Maximální celé číslo proto zapíšete 2147483647.

Zapisujete-li číslo v desítkové soustavě, nesmí začínat nulou – to by si počítač myslel, že zadáváte hodnotu v osmičkové soustavě.

V programech se občas čísla zapisují i v jiných číselných soustavách než v desítkové. K této možnosti se ale vrátím až v třetí části knihy, kdy si ukážeme situace, v nichž je takový zápis užitečný.

double S reálnými čísly je to trochu složitější – ta můžete zapsat dvěma různými způsoby. Buď jako obyčejné desetinné číslo, např. 3.1415926, nebo v tzv. semilogaritmickém tvaru:

Desetinné číslo

Oproti našim zvyklostem se v Javě (stejně jako v ostatních programova-cích jazycích) nepoužívá desetinná čárka, ale desetinná tečka.

Oproti celým číslům mohou desetinná čísla začínat i nulou.

Page 147: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 147

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 147 z 433

Lenoši ocení, že nemá-li číslo celou část, nemusíte před desetinnou tečku psát nic – např. číslo 0,123 můžete zapsat .123. Nicméně zápis 0.123 je také správný.

Semilogaritmický tvar (někdo tomuto tvaru říká „vědecký tvar“):

Sestává ze tří částí: mantisy, oddělovače exponentu a exponentu.

Mantisa je obyčejné desetinné číslo.

Jako oddělovač exponentu slouží písmeno e nebo E.

Exponent je celé číslo se znaménkem.

Při použití semilogaritmického tvaru můžete jedno a to samé číslo zapsat mnoha způsoby. Několik možných podob zápisu čísla 123,45 uvádí následu-jící tabulka. U každé podoby je přitom uveden její ekvivalent, jak by jej zapsal matematik.

Java Matematik Java Matematik

0.012345e+4 0,012345.104 .12345e+03 0,12345.103 1.2345e2 1,2345.102 12.235E01 12,345.101 123.45E0 123,45.100 1234.5e-1 1234,5.10-1 12345E-2 12345.10-2 123450e-3 123450.10-3

Celé číslo Jsou situace, kdy potřebujeme, aby počítač pracoval s celým číslem jako kdy-by bylo reálné. K tomu mám dvě možnosti:

Za číslo přidáme příponu D nebo d – např. 2D, 123d.

Číslo napíšeme jako desetinné, např. 2.0, 123.0.

String Datový typ String, tj. typ znakových řetězců, je jediný objektový datový typ, kte-rý má vlastní literál. Pro jeho psaní platí následující pravidla:

Literál vytvoříme tak, že zadávaný řetězec zapíšeme mezi uvozovky – např. "Zadávaný řetězec".

Celý řetězec musí být na jednom řádku.

Potřebujeme-li zadat dlouhý řetězec, který se nám nechce psát na jeden řá-dek, můžeme jej vytvořit z „jednořádkových“ částí, mezi něž vložíme zna-ménko + (znaménkem + ale můžeme spojovat i řetězce na témže řádku) např.: "Dlouhý řetězec, " + "který rozdělíme" +

Page 148: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

148 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 148 z 433

"na několik řádků," + "aby se nám lépe četl."

Potřebujeme-li vložit do řetězce přechod na nový řádek, vložíme do něj dvo-jici znaků \n – např. "První řádek\ndruhý řádek\ntřetí řádek".

Potřebujeme-li vložit do řetězce uvozovky, vložíme do něj dvojici \" – např. řetězec "Cituji: \"Bude dobře!\"" se vysadí jako: Cituji: ʺBude dobře!ʺ

Potřebujeme-li vložit do řetězce znak obráceného lomítka, vložíme dvojici obrácených lomítek – např. řetězec "Uvozovky: \\\"" se vytiskne jako Uvo-zovky: \ʺ.

null null je společným literálem pro všechny objektové datové typy. Jak jsme si již mnohokrát řekli, když máte atribut nebo proměnnou objektového typu, uchová-váte v ní odkaz na instanci daného typu. Občas je ale potřeba dát vědět, že v dané proměnné ještě žádný odkaz není. K tomu právě slouží hodnota null, která za-stupuje odkaz nikam.

V projektu 03_Třídy_Z najdete třídy Strom_3b, která obsahuje definice atributů a metod třídy Strom uvedené v kapitole až do této chvíle. Podle ní si můžete zkontrolovat, jestli jsou vaše soubory v pořádku. Kromě toho zde najdete i třídu Strom_3bTest, pomocí níž můžete třídu Strom_3b otestovat.

3.18 Komentáře a dokumentace

V této podkapitole (a samozřejmě i v některých dalších) budu občashovořit současně o atributech, konstruktorech i metodách. V takovém případě budu pro všechny používat termín členy. Kdybychom se toho-to označení drželi, mohli bychom o atributech hovořit jako o datových členech a o metodách a konstruktorech jako o funkčních členech nebo členských funkcích. Současně bychom mohli rozlišovat statické členya nestatické, neboli instanční členy. Já tyto složené termíny používat nebudu; uvádím je tu jen proto, že se s nimi můžete občas v literatuřesetkat.

Page 149: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 149

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 149 z 433

Programátorská zkušenost říká, že žádný program nevydrží věčně v podobě, ve které byl původně vyvinut. Prakticky každý program je po kratší či delší době vy-lepšován a upravován. Naše třída je sice relativně maličká, ale i tak se nám již utě-šeně rozrůstá a za chvíli bychom v ní mohli ztratit orientaci, zejména pokud bychom se k ní vrátili po delší době. A jak bychom teprve bloudili v případě, kdyby tato třída byla opravdu velká.

Programátoři proto doplňují své programy komentáři, ve kterých si pozna-menávají, proč třídu nadefinovali právě takto. Popisují v nich celkový účel dané třídy, funkci jednotlivých metod, význam jejich parametrů a řadu dalších důleži-tých vlastností. V tělech metod si pak poznamenávají některé nestandardní obra-ty, které by čtenářům kódu nemuseli být jasné.

Z hlediska překladače je komentář něco podobného jako mezera. Kamkoliv můžete v programu vložit mezeru, tam můžete stejně dobře vložit i komentář. Vý-jimkou je pouze vnitřek textových řetězců, jenže tam mezera nefunguje jako oddě-lovač jiných prvků programu, ale vystupuje tam jako znak.

Tři druhy komentářů Java zavádí tři druhy komentářů:

Řádkový komentář začíná dvojicí znaků // a končí spolu s koncem řádku. Řádkové komentáře používáme většinou tehdy, chceme-li doplnit poznám-kou nějaký kus kódu uvnitř těla konstruktoru nebo nějaké větší třídy.

Obecný komentář začíná dvojicí znaků /* a končí inverzní dvojicí */ – tyto dvojice slouží jako komentářové závorky. Vše, co mezi nimi překladač najde, ignoruje. Obecný komentář proto může zabírat několik řádků.

Dokumentační komentář je pouze speciálním typem obecného komentáře, který začíná trojicí znaků /**. Zapisuje se do nich dokumentace k vytváře-nému programu. Mezi programy obsaženými v sadě JDK je program javadoc.exe, který umí prohledat označené soubory, vypreparovat z nich do-kumentační komentáře a vytvořit z těchto komentářů řádnou dokumentaci.

Dokumentační komentáře se v programu zapisují těsně před dokumen-tovaný prvek (program javadoc reaguje i na umístění jednotlivých komentářů):

komentář popisující účel a použití třídy patří před hlavičku třídy,

komentář popisující účel nějakého atributu patří před deklaraci tohoto atributu,

komentář popisující funkci nějaké metody a význam jejích parametrů pa-tří před hlavičku této metody.

Page 150: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

150 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 150 z 433

Pokračovací řádky obecných a dokumentačních komentářů bývá dobrým zvykem začínat hvězdičkami. Ty sice nejsou povinné, ale zvyšují přehlednost programu, protože výrazně oddělují komentář od výkonného kódu. Proto je řada programá-torských editorů (mezi nimi i BlueJ) na počátek pokračovacích řádků automaticky vkládá.

Já ve svých programech navíc používám první řádek komentáře tvořený ce-lou řadou hvězdiček, který tak vizuálně odděluje jednotlivé metody. Je na vás, jestli tento zvyk převezmete a nebo si zavedete nějaké vlastní konvence.

Vhodné komentáře dokáží výrazně zvýšit čitelnost programu. Naučte se své programy dostatečně komentovat, abyste pak při pozdějších úpravách nemuseli pracně analyzovat, jak jste to tenkrát mysleli. Znovu bych zde zopakoval, že dob-rý programátor píše programy, kterým rozumí nejen počítač, ale i člověk. Správné komentování programů proto patří k dobrým programátorským mra-vům.

Vyzkoušíme si nyní použití komentářů na našem programu. V programu provedeme následující úpravy:

Před celou třídu, který bude popisovat účel třídy.

Před každou z metod vložíme dokumentační komentář popisující funkci da-né metody.

Dokumentační komentáře s popisem významu vložíme i před deklarace ve-řejných atributů.

Soukromé atributy doplníme řádkovými komentáři popisujícími jejich použi-tí.

Jednotlivé deklarace a definice v těle třídy navíc proložíme řádkovými ko-mentáři, které nám celé tělo rozdělí na sekce obsahující deklarace a definice stejného druhu objektů.

Mezi definice metod vložíme pro zvýšení přehlednosti dva prázdné řádky, za poslední deklaraci či definici před oddělujícím řádkovým komentářem vlo-žíme tři prázdné řádky. (V následujícím programu je pro úsporu místa vždy o jeden oddělující řádek méně.)

Za zavírací závorku těla třídy vložíme řádkový komentář, do nějž zkopíru-jeme hlavičku třídy. (Tuto grafickou úpravu využívám k tomu, abych měl jasně označenu závěrečnou zavírací závorku pro případ, kdyby počet oteví-racích a zavíracích závorek v programu nesouhlasil.)

Program Strom by po všech těchto úpravách (a drobné změně pořadí jednotlivých metod) mohl vypadat následovně:

Page 151: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 151

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 151 z 433

Abych nemusel program za chvíli ukazovat ještě jednou, použil jsem vněm i komentářové značky začínající znakem @. Podrobnosti o těchto značkách si povíme za chvíli v pasáži Pomocné značky pro tvorbu doku-mentace na straně 164.

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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

/******************************************************************************* * Třída Strom obsahuje podobu třídy po zavedení komentářů. * * Oproti předchozí verzi je zdrojový kód pouze okomentován. * Obsahuje dokumentační komentáře všech veřejných atributů a tříd. * Atributy a metody jsou navíc seřazeny podle doporučeného pořadí. * * @author Rudolf Pecinovský * @version 2.01, duben 2004 */ public class Strom { //== KONSTANTNÍ ATRIBUTY TŘÍDY ================================================= /** Udává kolikrát je strom vyšší než samotný kmen. */ public static final int IMPLICITNÍ_POMĚR_VÝŠKY = 3; /** Udává kolikrát je koruna širší než kmen. */ public static final int IMPLICITNÍ_POMĚR_ŠÍŘKY = 10; //== PROMĚNNÉ ATRIBUTY TŘÍDY =================================================== /** Velikost posunu pro bezparametrické posunové metody. */ private static int krok = 50; //== KONSTANTNÍ ATRIBUTY INSTANCÍ ============================================== private final Elipsa koruna; //Koruna stromu private final Obdélník kmen; //Kmen stromu //== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================//== PŘÍSTUPOVÉ METODY ATRIBUTŮ TŘÍDY ========================================== /*************************************************************************** * Vrátí velikost implicitního kroku, o který se instance přesune * při volaní bezparametrickych metod přesunu. * * @return Velikost implicitního kroku v bodech */ public static int getKrok() { return krok; } /*************************************************************************** * Nastaví velikost implicitního kroku, o který se instance přesune

Page 152: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

152 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 152 z 433

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

100 101 102 103 104 105 106 107

* při volaní bezparametrickych metod přesunu. * * @param velikost Velikost implicitního kroku v bodech;<br/> * musí platit: 0 <= velikost <= 100 */ public static void setKrok( int velikost ) { krok = velikost; } /*************************************************************************** * Metoda se dotáže uživatele na požadovanou velikost kroku používaného * v bezparametrických posunových metodách a zadanou hodnotu nastaví. */ public static void setKrok() { krok = P.zadej( "Zadejte novou velikost kroku:", krok ); } //== OSTATNÍ METODY TŘÍDY ====================================================== /*************************************************************************** * Vytvoří instanci zadané velikosti a upraví rozměr plátna tak, * aby byla na plátně právě zarámovaná. */ public static void zarámuj( int šířka, int výška ) { Plátno.getPlátno().setRozměr( šířka, výška ); new Strom( 0, 0, šířka, výška ); } /*************************************************************************** * Metoda upraví rozměr plátna a "vysadí" na něj alej dvou řad stromů * se třemi stromy v každé řadě. Stromy budou vysazeny šikmo ve směru * hlavní diagonály s kmenem zabírajícím 1/3 výšky a 1/10 šířky stromu. */ public static void alej() { Plátno plátno = Plátno.getPlátno(); plátno.setRozměr( 400, 350 ); new Strom( 100, 0 ); new Strom( 300, 0 ); new Strom( 50, 100 ); new Strom( 250, 100 ); new Strom( 0, 200 ); new Strom( 200, 200 ); } //== KONSTRUKTORY ============================================================== /*************************************************************************** * Implicitní konstruktor třídy Strom vytvoří v levém horním rohu plátna * instanci širokou 100 bodů, vysokou 150 bodů * s kmenem zabírajícím 1/3 výška a 1/10 šířky stromu. */ public Strom() { this( 0, 0 ); }

Page 153: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 153

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 153 z 433

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

/*************************************************************************** * Vytvoří na zadaných souřadnicích * instanci širokou 100 bodů, vysokou 150 bodů * s kmenem zabírajícím 1/3 výška a 1/10 šířky stromu. * * @param x x-ová souřadnice počátku, x>=0, x=0 má levý okraj plátna * @param y y-ová souřadnice počátku, y>=0, y=0 má horní okraj plátna */ public Strom( int x, int y ) { this( x, y, 100, 150 ); } /*************************************************************************** * Vytvoří na zadaných souřadnicích instanci se zadanou šířkou a výškou. * Poměr velikosti kmene ku zbytku stromu zůstane implicitní, tj. * kmen bude zabírat 1/3 výška a 1/10 šířky stromu. * * @param x x-ová souřadnice počátku, x>=0, x=0 má levý okraj plátna * @param y y-ová souřadnice počátku, y>=0, y=0 má horní okraj plátna * @param šířka Šířka vytvářené instance, šířka > 0 * @param výška Výška vytvářené instance, výška > 0 */ public Strom( int x, int y, int šířka, int výška ) { this( x, y, šířka, výška, IMPLICITNÍ_POMĚR_ŠÍŘKY, IMPLICITNÍ_POMĚR_VÝŠKY ); } /*************************************************************************** * Vytvoří na zadaných souřadnicích instanci se zadanou šířkou, výškou. * a poměrem velikosti kmene ku zbytku stromu. * * @param x x-ová souřadnice počátku, x>=0, x=0 má levý okraj plátna * @param y y-ová souřadnice počátku, y>=0, y=0 má horní okraj plátna * @param šířka Šířka vytvářené instance, šířka > 0 * @param výška Výška vytvářené instance, výška > 0 * @param podílVýškyKmene Kolikrát je kmen menší než celý strom * @param podílŠířkyKmene Kolikrát je kmen užší než celý strom */ public Strom( int x, int y, int šířka, int výška, int podílVýškyKmene, int podílŠířkyKmene ) { int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene; int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = ( šířka - šířkaKmene) / 2; koruna = new Elipsa ( x, y, šířka, výškaKoruny, Barva.ZELENÁ ); kmen = new Obdélník( x+posunKmene, y+výškaKoruny, šířkaKmene, výškaKmene, Barva.ČERVENÁ ); } //== PŘÍSTUPOVÉ METODY ATRIBUTŮ INSTANCÍ ======================================= /*************************************************************************** * Vrátí x-ovou souřadnici pozice instance.

Page 154: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

154 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 154 z 433

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

* * @return x-ová souřadnice. */ public int getX() { return xPos; } /*************************************************************************** * Vrátí y-ovou souřadnici pozice instance. * * @return y-ová souřadnice. */ public int getY() { return yPos; } /*************************************************************************** * Nastaví novou pozici instance. * * @param x Nová x-ová pozice instance * @param y Nová y-ová pozice instance */ public void setPozice(int x, int y) { { koruna.setPozice( x, y ); kmen .setPozice( x + (koruna.getŠířka() - kmen.getŠířka()) / 2, y + koruna.getVýška() ); koruna.nakresli(); } /*************************************************************************** * Vrátí šířku instance. * * @return Šířka instance v bodech */ public int getŠířka() { return koruna.getŠířka(); } /*************************************************************************** * Vrátí výšku instance. * * @return Výška instance v bodech */ public int getVýška() { return koruna.getVýška() + kmen.getVýška(); } /*************************************************************************** * Vrátí barvu koruny stromu. * * @return Instance třídy Barva definující nastavenou barvu koruny. */

Page 155: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 155

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 155 z 433

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

public Barva getBarvaKoruny() { return koruna.getBarva(); } /*************************************************************************** * Nastaví novou barvu koruny. * * @param nová Požadovaná nová barva. */ public void setBarvaKoruny( Barva nová ) { koruna.setBarva( nová ); } //== OSTATNÍ METODY INSTANCÍ =================================================== /*************************************************************************** * Vykreslí obraz své instance na plátno. */ public void nakresli() { koruna.nakresli(); kmen .nakresli(); } /*************************************************************************** * Smaže obraz své instance z plátna (nakreslí ji barvou pozadí plátna). */ public void smaž() { koruna.smaž(); kmen .smaž(); } /*************************************************************************** * Přesune instanci o zadaný počet bodů vpravo, * při záporné hodnotě parametru vlevo. * * @param vzdálenost Vzdálenost, o kterou se instance přesune. */ public void posunVpravo( int vzdálenost ) { koruna.posunVpravo( vzdálenost ); kmen .posunVpravo( vzdálenost ); } /*************************************************************************** * Přesune instanci o krok bodů vpravo. */ public void posunVpravo() { posunVpravo( krok ); } /*************************************************************************** * Přesune instanci o krok bodů vlevo.

Page 156: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

156 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 156 z 433

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

*/ public void posunVlevo() { posunVpravo( -krok ); } /*************************************************************************** * Přesune instanci o zadaný počet bodů dolů, * při záporné hodnotě parametru nahoru. * * @param vzdálenost Počet bodů, o které se instance přesune. */ public void posunDolů( int vzdálenost ) { koruna.posunDolů( vzdálenost ); kmen .posunDolů( vzdálenost ); koruna.nakresli(); } /*************************************************************************** * Přesune instanci o krok bodů dolů. */ public void posunDolů() { posunDolů( krok ); } /*************************************************************************** * Přesune instanci o krok bodů nahoru. */ public void posunVzhůru() { posunDolů( -krok ); } /*************************************************************************** * Nastaví parametry okna s plátnem tak, aby právě zarámovalo danou * instanci. Instanci před tím přesune do levého horního rohu plátna. */ public void zarámuj() { Plátno.getPlátno().setRozměr( getŠířka(), getVýška() ); setPozice( 0, 0 ); } }// public class Strom

Zdrojové kódy předchozí třídy najdete v projektu 03_Třídy_Z ve třídě Strom_3c. Podle ní si můžete zkontrolovat, jestli jsou vaše soubory v po-řádku. Kromě toho zde najdete i třídu Strom_3cTest, pomocí níž může-te třídu Strom_3c otestovat.

Page 157: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 157

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 157 z 433

Uspořádání jednotlivých prvků v těle třídy Jak vidíte, už je to docela pěkný macek a další programy budou ještě větší. Je pro-to nanejvýš vhodné zavést nějaké konvence, abyste potřebné členy ve zdrojovém souboru co nejrychleji našli.

Ve všech programech této učebnice bude proto tělo třídy rozděleno do oddí-lů, které budou odděleny jednořádkovými komentáři označujícími obsah následu-jícího oddílu a které budou mít ve všech dalších programech pevné pořadí. Přitom budu vycházet z následujících zásad:

Nejprve budou deklarovány atributy a teprve po nich metody.

Jak mezi atributy, tak mezi metodami dáme vždy dopředu statické členy a teprve za nimi budou členy nestatické.

Konstanty budou uvedeny dříve než proměnné.

Konstruktory budou umístěny uprostřed mezi statickými a nestatickými me-todami. Statické metody budou před nimi proto, že k jejich zavolání není po-třeba mít definovanou žádnou instanci, a nestatické za nimi, protože před jejich použitím je potřeba nejprve použít konstruktor, který nějakou instanci vytvoří.

Mezi konstruktory zařadíme i metody, které vracejí odkaz na instanci vlastní třídy (sem patří např. metoda getPlátno). K těmto metodám se ještě vrátím v kapitole Jedináček (Singleton) na straně 233.

Mezi metodami uvedeme vždy nejprve metody, které nastavují nebo vracejí hodnoty atributů (byť zdánlivých) a teprve za nimi ostatní metody.

V programu jsou tedy sekce uspořádané v následujícím pořadí:

1. konstantní atributy třídy, tj. statické konstanty,

2. ostatní atributy třídy,

3. konstantní atributy instancí,

4. ostatní atributy instancí,

5. přístupové metody atributů třídy,

6. ostatní metody třídy,

7. konstruktory a metody vracející odkaz na instance vlastní třídy,

8. přístupové metody atributů instancí,

9. ostatní metody instancí.

Page 158: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

158 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 158 z 433

Sekce s ostatními metodami instancí je ještě podrobněji dělena, ale totodělení si probereme až poté, co si vysvětlíme příslušné konstrukce (bu-deme se s nimi postupně seznamovat v průběhu celé následující části). Prozatím budeme používat pouze „Nově zavedené metody instancí“.

BlueJ a komentářová nápověda BlueJ je schopen využít dokumentačních komentářů jako nápovědy při volání konstruktorů a metod. Jak víte, voláte-li nějaký konstruktor nebo metodu, která očekává parametry, otevře BlueJ dialogové okno, ve kterém zadáte názve odkazu na vytvořenou instanci a/nebo hodnoty parametrů. Do horní části tohoto dialogo-vého okna BlueJ opíše dokumentační komentář.

Obrázek 3.29 Při volání konstruktorů a metod s parametry se do dialogového okna

opíše dokumentační komentář

Dokumentační komentáře však neslouží pouze k tomu, aby se jejich obsah vypsal do nějakých dialogových oken. Ony slouží opravdu k vytvoření dokumentace. O tom se můžete přesvědčit např. tak, že rozbalíte seznam na pravém okraji panelu s tlačítky a místo dosavadní položky Implementace (zdrojový kód) vyberete položku Do-kumentace (popis rozhraní) (viz obr. 3.30). BlueJ pak zavolá program, který je součástí JDK a který z vašich dokumentačních komentářů vyrobí HTML soubor efektně dokumentující vaši třídu (viz obr. 3.31).

Page 159: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 159

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 159 z 433

Obrázek 3.30 Editor umožňuje přepínat mezi zobrazením kódu a vygenerované dokumentace

Page 160: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

160 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 160 z 433

Obrázek 3.31 Z dokumentačních komentářů se přímo vygeneruje profesionální dokumentace

Automaticky generovaná dokumentace Tato autodokumentační schopnost Javy je jednou z jejích velice příjemných vlast-ností. Při generování dokumentace však Java (přesněji program javadoc) nezůstává pouze u toho, že někam opíše obsah dokumentačních komentářů. Jak si můžete na vygenerovaném souboru ověřit, vygenerovaná dokumentace sestává z několi-ka částí:

1. Popis vlastností a použití dané třídy získaný z dokumentačního komentáře před hlavičkou třídy.

Page 161: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 161

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 161 z 433

2. Tabulky, které shrnou základní informace o dostupných atributech, kon-struktorech a metodách, přičemž každá skupina prvků má vlastní tabulku. U každého členu je uvedena pouze první věta z jeho komentáře (viz např. ko-mentář čtyřparametrického konstruktoru nebo metody alej()) a prvky jsou v tabulkách řazeny abecedně.

3. Úplné komentáře o všech atributech, konstruktorech a metodách. BlueJ zařídí i u těchto popisů jejich seřazení podle abecedy, ale v praxi bývají tyto popisy většinou seřazeny ve stejném pořadí, v jakém jsou uvedeny v programu. I ta-to část je rozdělená na sekce věnované postupně atributům, konstruktorům a metodám.

Takto vygenerovaná dokumentace je uložena do souborů ve formátu HTML v podsložce doc složky, ve které máte uložen projekt.

Jednotlivé části dokumentace jsou spolu provázané hypertextovými odkazy, jež jsou jednou z příjemných vlastností formátu HTML. Tu podporuje i editor prostředí BlueJ. Klepnete-li v tabulce atributů, konstruktorů nebo metod na název vybraného objektu, přesunete se automaticky do sekce, kde si můžete přečíst jeho podrobnou dokumentaci. Editor však, na rozdíl od webových prohlížečů, nenabí-zí jednoduchou možnost vrátit se zpět na místo, odkud jste sem skočili.

Všimněte si, že v dokumentaci jsou uvedeny pouze členy deklarované jako veřejné. Neobjevují se v ní proto např. soukromé atributy. Kdybys-te některou z metod označili jako soukromou, v následně vygenerované dokumentaci by se již také neobjevila (vyzkoušejte si to).

Dokumentace celého projektu V prostředí BlueJ nemusíte vytvářet dokumentaci každé třídy zvlášť, ale můžete BlueJ požádat, ať vám vygeneruje dokumentaci celého projektu. Toho dosáhnete klávesovou zkratkou CTRL+J nebo zadáním příkazu Nástroje → Dokumentace projektu v aplikačním okně daného projektu (viz obr. 3.32).

Page 162: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

162 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 162 z 433

Obrázek 3.32 BlueJ můžete požádat i o vytvoření dokumentace k celému projektu

Dokumentace projektu se již neotevírá v okně editoru, ale v okně vašeho prohlí-žeče, protože se v ní používají rámy: v levém je zobrazen seznam všech tříd v pro-jektu, v pravém pak dokumentace konkrétní třídy (viz obr. 3.33). Klepnutím na název třídy v levém rámu otevřete její dokumentaci v rámu pravém.

Výhodou otevření dokumentace v prohlížeči je i to, že nyní můžete svobodně vy-užívat všech možností hypertextových odkazů včetně možnosti návratu do místa, odkud jsme sem skočili nebo otevření cíle v novém okně. Uložíte-li si navíc tuto stránku mezi své oblíbené webové stránky, budete ji moci otevřít kdykoliv si vzpomenete nezávisle na tom, máte-li zrovna otevřené prostředí BlueJ či nikoliv.

Page 163: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 163

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 163 z 433

Obrázek 3.33 Dokumentace projektu se otevírá v okně prohlížeče,

protože obsahuje rám se seznamem tříd a rám s dokumentací vybrané třídy

Dokumentace projektu obsahuje v záhlaví řadu dalších odkazů, které vám umož-ní se po ní snadno pohybovat. Jejich funkci tu nebudu rozebírat. Jste-li zvídaví, vyzkoušejte si ji.

Page 164: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

164 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 164 z 433

Pomocné značky pro tvorbu dokumentace Podíváte-li se podrobněji na dokumentaci metod z předdefinovaných tříd, zjistíte, že obsahuje některé dodatečné informace, o něž je dokumentace třídy Strom pro-zatím ochuzena.

Podíváte-li se např. na podrobný popis konstruktorů a metod s parametry, najdete zde sekci Parameters: s popisem jednotlivých parametrů. Obdobně podí-váte-li se na podrobný popis metod vracejících nějakou hodnotu, najdete zde sekci Returns: s popisem vracené hodnoty. Generaci těchto sekcí vyvolají tzv. doku-mentační značky, které jsou součástí příslušných dokumentačních komentářů.

Dokumentačních značek je velké množství. My se zde seznámíme pouze s několika z nich. Až budete zkušenější, vaše znalosti značek opět rozšíříme.

Nebudu zde uvádět ani příklady použití dále popsaných značek – najdete je ve zdrojových textech předdefinovaných tříd.

Každá z následně uvedených značek musí být na počátku řádku (může ale stát za úvodní hvězdičkou). Vztahuje se k ní veškerý zbylý text na řádku spolu s texty na dalších řádcích až do řádku s příští značkou nebo do konce komentáře.

@author Tato značka se uvádí v dokumentaci k celé třídě. Zapisuje se za ní název autora dané třídy.

@version I tato značka se uvádí v dokumentaci k celé třídě. Zapisuje se za ní číslo verze. Pro formát verze není žádný předpis – můžete jej zadat třeba slovně i s popisem změn oproti verzi minulé.

@param Značka, kterou použijete v dokumentaci konstruktorů a metod s parametry. Za ní je třeba uvést přesný název parametru z hlavičky příslušného konstruktoru či me-tody a za něj pak jeho popis.

@returns Tuto značku použijete v dokumentaci metod, které vracejí nějakou hodnotu. Uvádí se za ní podrobný popis návratové hodnoty.

Page 165: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 165

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 165 z 433

3.19 Závěrečný příklad – UFO Do této doby jsme si stále jenom ukazovali, jaké má to či ono vlastnosti, jak se to naprogramuje případně kde nás čeká nějaká záludnost. Samé učení a žádná zába-va. Na závěr kapitoly jsem proto pro vás připravil velkou závěrečnou úlohu, při které si ověříte, že jste se toho již opravdu hodně naučili a zároveň se možná i tro-chu pobavíte.

Naprogramujete si totiž vlastní jednoduchou hru. Nenaprogramujete ji sice celou, ale pouze jednu její část, nicméně i v praxi bývá hlavním úkolem programá-tora vytvořit nějakou část rozsáhlejšího projektu.

Připravil jsem pro vás projekt 03_UFO, ve kterém najdete pět připravených tříd, jednu prázdnou třídu pro svoje řešení a jednu třídu se vzorovým řešením pro ty, kteří se dostanou do nějakých problémů. Projekt obsahuje následující třídy:

Vesmír – její instance má na starosti vytvoření a zobrazení aplikačního okna, představujícího vesmír, v němž se celá hra odehrává. Tato třída je v aplikaci jako služební a vaše třída s ní komunikovat nebude.

Barva – třída, kterou znáte z naší aplikace pracující s grafickými objekty.

Dispečer – klíčová třída celé aplikace. její instance má na starosti otevření pří-slušného vesmíru a řízení veškerého provozu v něm. Tuto instanci vždy po-žádáte o přistavení nového UFO na startovací rampu, odkud je máte dovést do některého z hangárů.

Talíř – představuje jednu z částí UFO. Nabízí jedinou metodu, pomocí které můžete nastavit jeho pozici.

Číslo – instance této třídy je schopna zobrazit číslo, které jí zadáte při kon-strukci. Instance této třídy slouží k identifikaci UFO a přistávacích ramp (hangárů). Instance třídy Číslo sice nabízejí metod více, ale vy nejspíš použi-jete pouze metodu pro nastavování jejich pozice.

UFO_RUP – třída se vzorovým řešením. Jako obyčejně vám doporučuji, abyste se do ní podívali až v případě, kdy budete mít nějaké problémy nebo když budete hotoví a budete chtít porovnat svoje řešení se vzorovým.

UFO – třída, kterou máte definovat. Obsahuje komentované prázdné definice všech metod, které musíte vytvořit.

Page 166: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

166 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 166 z 433

Třída Dispečer Jak jsem již řekl, třída Dispečer je klíčovou třídou celé aplikace. Chová se obdobně jako třída Plátno – protože chce, aby její instance byla jedináček, nenabídne vám konstruktor, ale pouze metodu getDispečer(), která vrátí odkaz na aktuálního dispečera. Pokud ještě dispečer ještě není vytvořen, tak jej vytvoří spolu s vesmí-rem, který bude tímto dispečerem ovládán.

Dispečer může pracovat ve dvou režimech a tak ovládat dvě varianty hry. V první jednodušší variantě ovládáte svá UFO prostřednictvím BlueJ. tj. obdobně, jako jste doposud ovládali Strom a další grafické tvary, v druhé variantě ovládáte pohyb UFO a vůbec celou hru přímo z klávesnice.

Jednodušší varianta Při této variantě si musíte uložit do zásobníku odkaz na vytvořeného dispečera, abyste jej mohli vždy požádat o přistavení nového UFO na startovací rampu. Žá-dáte jej zasláním zprávy přistavUFO(), po níž dispečer přistaví na rampu nové UFO vrátí odkaz na něj.

Získaný odkaz uložíte do zásobníku odkazů a vhodným zadáváním rychlosti pomocí zprávy setRychlost(x,y) ovládáte rychlost UFO a snažíte se je zaparkovat v nějakém hangáru. Vjedete-li do hangáru dostatečně pomalu, měly by přistávací mechanizmy zabezpečit jeho automatické zaparkování.

Aby se vám podařilo UFO zaparkovat, nesmí mít při vjezdu do hangáru větší součet rychlostí ve vodorovném a svislém směru, než je velikost jeho talíře, která je přednastavena na 20 bodů. (Když ji ve třídě Talíř změníte, změníte tím i roz-měry celého vesmíru).

Jestli jste šikovní, můžete si vyzkoušet, zda dokážete ovládat několik UFO současně. Odhadnete-li dobře rychlost, abyste ji nemuseli následně korigovat, můžete zkusit, kolik UFO se vám podaří poslat najednou.

Varianta ovládaná z klávesnice Zvolíte-li variantu ovládanou z klávesnice, musíte nejprve aktivovat okno vytvo-řeného vesmíru (třeba tím, že na něj klepnete myší). Nebude-li okno aktivní, ne-bude na stisky kláves reagovat.

Při této variantě ovládáte kurzorovými klávesami tah motorů. Stisknete-li např. klávesu se šipkou doprava, začne UFO zrychlovat doprava. S každým dal-ším stiskem zvyšujete zrychlení.

Stiskem mezerníku vypínáte motory. Od té chvíle se bude UFO pohybovat konstantní rychlostí (ve vesmíru není tření). Budete-li je chtít zabrzdit, musíte za-

Page 167: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 167

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 167 z 433

pnout motory v opačném směru. Nesmíte je však zapomenout zase vypnout, až se UFO zastaví, jinak vám bude pokračovat ve zrychlování opačným směrem.

O přistavení dalšího UFO požádáte stiskem klávesy ENTER. Na klávesnici re-aguje vždy naposledy přistavené UFO. Budete-li ovládat několik UFO současně, zapnete ovládání příslušného UFO stiskem klávesy s jeho číslem.

Třída UFO Součástí projektu je i třída UFO, která má sice definovány všechny potřebné meto-dy, ale má je definovány jako prázdné (tj. s prázdným tělem, jehož definice je va-ším úkolem). Každá z těchto prázdných metod je doplněna dokumentačním komentářem, v němž je popsána její požadovaná funkce.

Řekl bych, že naprogramování většiny z nich by vám nemělo dělat potíže. Pro jistotu si ale zopakujeme trochu fyziky, abychom si ujasnili, jak by měla fun-govat metoda popojeď(int), jejíž funkce by mohla dělat některým z vás problémy. Ze své zkušenosti vím, že většina začínajících studentů programování by ráda programovala počítačové hry, ve kterých se to bude hemžit animovanými před-měty. Patříte-li mezi ně, mohl by být pro vás následující drobný teoretický výklad užitečný.

Ve škole jste se učili, že rychlost je definována jako dráha uražená za jednot-kový čas. Rychlost animovaných předmětů má dvě složky: vodorovnou a svislou. Vodorovná složka rychlosti říká, jak rychle se předmět posouvá ve vodorovném směru a svislá složka popisuje rychlost ve směru svislém.

Má-li tedy nějaký animovaný předmět definovanou rychlost jako počet bodů, o které se má posunout za sekundu, změní se jeho pozice každou sekundu o po-žadovaný počet bodů. Budeme-li však chtít, aby se předmět posouval relativně plynule, nemůžeme jeho pozici měnit jednou za sekundu, musíme ji měnit častěji.

U animovaných obrázků se často udává frekvence jejich překreslování. Ta ří-ká, kolikrát se obrázek za sekundu překreslí. Má-li se tedy předmět přesouvat ve vodorovném směru rychlostí rx bodů za sekundu, musí se jeho pozice změnit mezi dvěma překresleními o rx/frekvence bodů.

O našich UFO jsme si říkali, že jsou ovládána raketovými motory, které neo-vládají přímo jejich rychlost, ale řídí jejich zrychlení. Jsou-li motory vypnuty, po-hybuje se raketa ve vesmíru stále stejnou rychlostí (na rozdíl od raket, které známe z filmů, jejichž tvůrci neznají fyziku). Jakmile motory zapnu, budu raketa neustále zrychlovat či zpomalovat. Zůstane-li brzdící motor zapnut i poté, co ra-ketu zastavím, stane se z něj motor urychlovací a raketa začne zrychlovat opač-ným směrem.

Se zrychlením je to obdobně jako s rychlostí. Zrychlení je definováno jako změna rychlosti za jednotku času. Má-li předmět zrychlovat ve vodorovném smě-

Page 168: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

168 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 168 z 433

ru se zrychlením zx, musí se jeho vodorovná rychlost změnit mezi dvěma překres-leními obrazovky o zx/frekvence.

Označíme-li vodorovnou pozici objektu jako px, budeme při nenulovém zrychlení muset pří výpočtu jeho budoucí pozice počítat se stále se zvyšující rych-lostí, takže tuto pozici zjistíme následujícího dvojicí příkazů: rx = rx + (zx / frekvence); px = px + (rx / frekvence);

Obdobné to bude i se svislou pozicí, rychlostí a zrychlením. Myslete na to při de-finici metody popojeď(int), která má na starosti přesun vašeho UFO mezi jednot-livými překresleními vesmíru.

Třída UFOTest Abyste mohli průběžně kontrolovat svůj návrh třídy UFO, připravil jsem třídu UFOTest, která obsahuje několik testovacích metod.

Metoda testUFO() postupně prověří většinu metod instanci třídy UFO. Testuje konstruktor, metody pro zjištění jeho tahu, rychlosti a pozice a funkci metod setRychlost(int,int) a popojeď(int).

Metoda testRychlost() otestuje také schopnost metody zobraz(). Nechá vy-tvořit dispečera, pověří jej přistavením pěti UFO a ta pak doprovodí do jejich hangárů. Bude-li všechno fungovat jak má, mělo by na konci být v každém hangáru zaparkované jedno UFO.

Metoda testHra() vytvoří dispečera (a s ním i příslušný vesmír) a spustí tak hru. Stejného výsledku však dosáhnete i tím, že třídě Dispečer pošlete zprávu getDispečer().

3.20 Vytvoření samostatné aplikace Když už jste si vytvořili vlastní aplikaci, ukážeme si, jak lze zařídit, abyste ji mohli spouštět stejně jako ostatní aplikace a nepotřebovali jste k jejímu spuštění otevírat nejprve BlueJ.

Java umožňuje zapakovat celou aplikaci do speciálního souboru a ten pak spouštět obdobně, jako se spouští standardní spustitelné soubory. Jedinou pod-mínkou pro jeho spuštění je instalovaná Java. Nemusí to ale být kompletní sada pro vývojáře (SDK), kterou jste si museli instalovat vy, ale stačí jenom běhové

Page 169: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 169

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 169 z 433

prostředí JRE (Java Runtime Environment), které je výrazně menší (14,5 MB oproti 50 MB SDK).

Tento zapakovaný soubor má příponu JAR, což je zkratka z Java ARchive. JAR soubor je vlastně obyčejný ZIP soubor, který obsahuje kromě přeložených tříd dané aplikace ještě složku META-INF se souborem MANIFEST.MF, v němž jsou uloženy některé informace důležité pro spuštění souboru.

Třída spouštějící aplikaci Aplikace, která má být spustitelná ze systému, musí obsahovat třídu s veřejnou statickou metodou main, která má parametr typu String[] (co znamenají ty hrana-té závorky se dozvíte ve třetí části učebnice). Tuto metodu můžete dodat do kte-rékoliv ze svých tříd, ale často bývá nejjednodušší vytvořit speciální třídu, která nebude mít na starosti nic jiného než spuštění vaší aplikace.

Svoji aplikaci však můžete o spouštěcí třídu jednoduše doplnit – stačí požá-dat o vytvoření nové třídy a v následně otevřeném dialogovém okně nastavit pře-pínač typu vytvářené třídy na Třída spouštějící aplikaci.

Název této třídy může být libovolná, ale doporučuji vám, abyste si zavedli nějakou konvenci, kterou budete v budoucnu dodržovat a tím si zjednodušíte ori-entaci v programu. Tuto třídu můžete např. nazývat Hlavní nebo Aplikace. Já ji budu ve zbytku této učebnice nazývat Hlavní, protože musí povinně obsahovat metodu main. Vy se ale můžete rozhodnout podle svého.

Otevřete zdrojový kód nově vytvořené spouštěcí třídy a doplňte do její meto-dy main potřebný kód, kterým budete aplikaci spouštět. V našem případě je tímto kódem vyvolání metody Dispečer.getDispečer(). 1 2 3 4 5 6 7 8

public class Hlavní { public static void main( String [] args ) { /*# Sem vložte kód, kterým se bude spouštět celá aplikace */ Dispečer.getDispečer(); } }//public class Hlavní

Celý projekt pak přeložte a tím jste připraveni k druhému kroku.

Vytvoření souboru JAR s aplikací Vytvoření spustitelného souboru je pak jednoduché:

1. Zadejte příkaz Projekt → Export….

Page 170: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

170 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 170 z 433

2. V následně otevřeném dialogovém okně BlueJ: Exportovat projekt (viz obr. 3.34) zadejte:

Přepínač nastavte do polohy Uložit do jar souboru.

V rozbalovacím seznamu Třída s metodou main: vyhledejte třídu, která bude zodpovědná za spuštění aplikace (v našem příkladu je to třída Hlavní).

Nastavte zaškrtnutí políčka Přidružit zdrojové soubory podle svých požadav-ků. Zaškrtnete-li je, přidají se do archivu i zdrojové soubory. Archiv pak bude sice větší, ale budete z něj moci zdrojové soubory kdykoliv vy-jmout a upravit. Nezaškrtnete-li je, přijdete o možnost budoucího upra-vování zdrojových souborů, ale získáte zase menší archiv. Prozatím bych vám doporučoval dát přednost kratšímu souboru a políčko nezaškrtávat.

Obrázek 3.34 Dialogové okno BlueJ: Exportovat projekt

3. Stiskněte tlačítko Pokračovat. Otevře se dialogové okno Specify name for jar file, ve kterém zadáte umístění a název ukládaného souboru. Název souboru můžete zadat bez přípony JAR. Teoreticky nemusí dodržovat ani pravidla pro názvy identifikátorů a stačí, bude-li to platný název souboru. Myslete však na to, že budete-li chtít poslat tento soubor kamarádům používajícím jinou platformu, mohli by mít s některými názvy problémy. Proto bych doporučoval zůstat u znakové sady pro identifikátory.

4. Vyzkoušejte, že váš program opravdu funguje jako normální aplikace. Nebu-de-li jej možno spustit poklepáním nebo jiným ze způsobů, na které jste zvyklí, nebudete mít asi správně asociovánu příponu jar. Pokud jste však in-stalovali Javu standardním způsobem, bude součástí instalace a správně aso-ciování přípony.

Page 171: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 171

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 171 z 433

3.21 Shrnutí – co jsme se v kapitole naučili

Deklarací nazýváme oznámení toho, že v dalším programu budu používat nějakou proměnnou, atribut či metodu. Při té příležitosti oznamuji i vlastnos-ti této metody, proměnné či atributu, aby mne mohl překladač kontrolovat, jestli můj program není s těmito deklarovanými vlastnostmi v rozporu.

Definicí se nazývá část programu, která specifikuje nejenom vlastnosti dané entity, ale definitivně ji v programu zavádí, tj. vyhrazuje pro ni paměť a při-řazuje jí počáteční hodnotu. Proměnná je proto definována v okamžiku, kdy jej jí přiřazena počáteční hodnota, metoda je definována v místě, kde je defi-nováno její tělo.

Velbloudí notací označujeme způsob zápisu, při němž se několikaslovný název píše dohromady bez mezer jako jedno slovo, přičemž každé slovo názvu za-číná velkým písmenem a ostatní písmena jsou malá – např. StrčPrtstSkrzKrk.

Názvy tříd píšeme podle konvence velbloudí notací s prvním písmenem vel-kým – např. StrčPrtstSkrzKrk nebo TřídaSDlouhýmNázvem.

Názvy atributů, proměnných a metod píšeme velbloudí notací s prvním pís-menem malým – např. strčPrtstSkrzKrk nebo atributSDlouhýmNázvem.

Nová třída se vytvoří klepnutím na tlačítko Nová třída a zadáním názvu a dru-hu vytvářené třídy v následně otevřeném dialogovém okně.

Zdrojový kód veřejné třídy je uložen v souboru, který má stejný název jako třída (musí se dodržet i velikost písmen) a má příponu .java.

Třídu odstraníme zadáním příkazu Odstranit v místní nabídce třídy.

Definice třídy sestává z hlavičky a těla. Hlavička obsahuje modifikátory blíže specifikující vlastnosti třídy (prozatím známe jen modifikátor public), klíčové slovo class a název třídy. Tělo následuje za hlavičkou, je uzavřeno ve slože-ných závorkách a obsahuje definice všech atributů a metod dané třídy.

Po každé úpravě zdrojového kódu je třeba třídu před jejím prvním použitím přeložit.

Proces, při němž hledáme a odstraňujeme chyby v programu se nazývá ladě-ní.

Page 172: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

172 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 172 z 433

V programech rozeznáváme tři druhy chyb: syntaktické, běhové a logické (sé-mantické). Syntaktické chyby objeví překladač při překladu, běhové způsobí zhavarování programu za běhu, logické se projeví nesprávným výsledkem, který však nemusíme na první pohled poznat.

Objeví-li překladač při překladu nějakou chybu, oznámí nám to ve spodním, informačním poli okna editoru.

Není-li nám význam chyby zřejmý, můžeme požádat o nápovědu stiskem tlačítka s otazníkem na pravém kraji informačního pole.

Pro efektivní vývoj programů je výhodné napsat nejprve testy a teprve pak testovaný program. Celý vývoj pak směřuje k tomu, abychom tyto testy „roz-chodili“. Tuto metodiku nazýváme vývoj programů řízený testy.

BlueJ umožňuje poloautomatickou tvorbu testů, při níž sleduje naše počínání a na požádání je uloží jako program.

Při testech využíváme testovací přípravek (test fixture), který připraví před každým testem předem definované počáteční podmínky a po testu zase v případě potřeby vše „uklidí“.

Definice konstruktoru sestává z hlavičky a těla. Hlavička obsahuje modifiká-tory následované názvem konstruktoru, který je shodný s názvem třídy, a se-znamem parametrů uzavřeným v kulatých závorkách. Nemá-li konstruktor žádné parametry, budou závorky prázdné.

Má-li být konstruktor viditelný zvenku třídy, označíme jej modifikátorem public.

Parametr deklarujeme tak, že uvedeme jeho typ (u objektových parametrů uvedeme jako jejich typ třídu, na jejíž instanci odkazují) následovaný názvem (identifikátorem) parametru, prostřednictvím nějž se k němu budeme v těle konstruktoru obracet.

Je-li pro nás výhodné zavolat v konstruktoru jiný konstruktor téže třídy, za-voláme jej tak, že napíšeme klíčové slovo this následované seznamem para-metrů. Překladač pozná, který konstruktor chceme zavolat, podle počtu a typu parametrů.

Volání konstruktoru this musí být úplně prvním příkazem těla konstruktoru, před ním smějí být již pouze mezery a komentáře.

Pro přesný zápis pravidel, podle nichž se vytváří (zapisuje) vysvětlovaná konstrukce, používáme syntaktické definice.

Page 173: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 173

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 173 z 433

Zaslání zprávy objektu realizujeme zavoláním odpovídající metody.

Metody se musí navzájem lišit svým jménem (identifikátorem) a/nebo se-znamem typů parametrů. Metody (včetně konstruktorů), které se liší pouze počtem a/nebo typem parametrů označujeme za přetížené.

Definice metody sestává z hlavičky a těla. Hlavička obsahuje modifikátory ná-sledované typem návratové hodnoty, názvem metody a seznamem paramet-rů uzavřeným v kulatých závorkách.

Metody, které nic nevrací, mají jako typ návratové hodnoty uveden typ void.

Metody vracejí požadované hodnoty tak, že jako poslední vykonávaný příkaz uvedou příkaz return následovaný výrazem, jehož hodnotu metoda vrací.

Jednotlivé verze přetížených metod budeme rozlišovat uvedením seznamu typů parametrů v závorkách za názvem metody.

Atributy deklarujeme v těle třídy, avšak mimo těla jejich metod.

Deklarace atributů sestává ze seznamu modifikátorů následovaného typem definovaného atributu a jeho identifikátorem (názvem) a případným přiřaze-ním počáteční hodnoty.

Atributy s výjimkou konstant, které není možné změnit, označujeme modifi-kátorem private.

U tříd rozeznáváme jejich rozhraní, tj. to, co o sobě třída zveřejní a na co se mohou její uživatelé spolehnout, a implementaci, tj. to, jak třída zařídí, že umí to, co vyhlásila v rozhraní.

Implementační detaily bychom měli před okolím skrývat, aby nebylo možno funkčnost třídy a jejích instancí ohrozit.

Vedle explicitně deklarované části rozhraní, tj. hlaviček veřejných metod, je součástí rozhraní i tzv. kontrakt popisující rysy, které není možno přímo spe-cifikovat prostředky jazyka.

Použité metody a atributy musíme vždy kvalifikovat, tj. napsat před ně název odkazu na instanci (u atributů a metod třídy můžeme použít název třídy), o jejíž metodu nebo atribut se jedná.

Kvalifikaci můžeme vynechat pouze v případě, kdy se na danou metodu či atribut obracíme v metodě instance (třídy), na jejíž metodu či atribut se obra-címe.

Page 174: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

174 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 174 z 433

Chceme-li zdůraznit, že se obracíme na atribut či metodu té instance, jejíž me-todu právě definujeme, kvalifikujeme ji klíčovým slovem this.

V případě potřeby můžeme uvnitř metody deklarovat lokální proměnné. De-klarace musí obsahovat typ proměnné (u objektových typů třídu instance, na níž bude proměnná odkazovat), identifikátor (název) a případně i přiřazení počáteční hodnoty.

Lokální proměnnou nelze použít, dokud se jí nepřiřadí nějaká hodnota.

Po ukončení metody jsou všechny její lokální proměnné ztraceny. Potřebuje-li si metoda něco pamatovat mezi svými spuštěními, musí si to uložit do něja-kého atributu.

Atributy a metody třídy definujeme tak, že mezi jejich modifikátory uvedeme klíčové slovo static.

Atributy a metody třídy bývají často označovány jako statické.

Má-li být hodnota atributu neměnná (konstantní), uvedeme mezi jeho modi-fikátory klíčové slovo final.

Statickým konstantám je třeba přiřadit jejich hodnotu již v deklaraci, nestatic-kým konstantám je možno přiřadit počáteční hodnotu v konstruktoru.

Jako veřejné by měly být z doposud probraných typů deklarovány pouze konstanty číselných typů, logické konstanty a konstanty typu String.

Metody mohou mít i parametry objektových typů. Při zadávání takovýchto parametrů v prostředí BlueJ můžeme využít možnosti zadat takovýto para-metr klepnutím na příslušný odkaz v zásobníku odkazů.

Jako literály označujeme hodnoty přímo zapsané do programu, tj. přímo za-daná čísla, textové řetězce a konstanty true, false a null.

Prázdný řetězce "" a prázdný odkaz na řetězec (null) jsou různé věci a je potřeba je rozlišovat.

Prázdný řetězec je exitující řetězec, který neobsahuje žádný znak. Naproti tomu prázdný odkaz je odkaz, který nikam neukazuje.

Jazyk Java používá dva druhy komentářů: obecný, který je ohraničen komen-tářovými závorkami /* a */, a řádkový, který začíná znaky // a končí spolu s koncem řádku.

Komentář můžeme napsat kdekoliv, kde můžeme napsat mezeru.

Page 175: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 3: Vytváříme vlastní třídu 175

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 175 z 433

Obecný komentář začínající znaky /** je chápán jako dokumentační. Zapisují se do něj informace užitečné pro budoucí uživatele dané třídy, metody, kon-stanty atd.

Vývojové prostředí Javy obsahuje program, který projde zdrojový kód a z dokumentačních komentářů vytvoří standardní dokumentaci.

V okně editoru můžeme zadat, zda má editor zobrazit implementaci dané třídy (zdrojový kód) nebo její rozhraní (dokumentaci). Při žádosti o zobrazení dokumentace BlueJ vyvolá program, který dokumentaci automaticky vytvoří.

Dokumentační komentáře musíme napsat těsně před dokumentovanou kon-strukci (třídu, atribut, metodu).

Dokumentační komentáře mohou vedle prostého textu obsahovat i HTML značky (tag).

Java zavádí několik speciálních značek začínajících znakem „@“, které slouží k lepšímu popisu některých rysů dokumentovaných konstrukcí, např. para-metrů či návratových hodnot dokumentovaných metod.

Zadáním příkazu Nástroje → Dokumentace projektu v okně projektu požádáme o vygenerování dokumentace všech tříd v projektu. Tuto dokumentaci si pak můžeme prohlížet HTML prohlížečem nezávisle na spuštění BlueJ.

Má-li být aplikace spustitelná i mimo BlueJ, musí obsahovat třídu s veřejnou statickou metodou main s parametrem typu String[].

Takovouto třídu můžeme jednoduše vytvořit přímo v BlueJ stiskem tlačítka Nová třída a následným nastavením přepínače Typ třídy na Třída spouštějící aplikaci. V takto vytvořené třídě pak stačí zapsat do metody main kód pro spuštění aplikace.

Vytvořené aplikace se pakují do souborů s příponou jar, které je pak možno spouštět obdobně jako soubory exe.

Aplikaci zapakujeme zadáním příkazu Projekt → Export a nastavení příslušných voleb.

Page 176: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

176 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 176 z 433

4. Dotváříme vlastní třídu

Kapitola 4 Dotváříme vlastní třídu

☯ Co se v kapitole naučíme V této kapitole náš projekt ještě neopustíme. Ve třetí kapitole jsme se naučili naprogramovat konstrukce, s nimiž jsme se seznámili ve druhé kapitole. Nyní rozšíříme svůj repertoár o dovednosti, s jejichž protějšky jsme se sice při našich hrátkách v druhé kapitole nesetkali, ale které nicméně patří do základního rejstříku dovedností objektově orientova-ného programátora.

V této kapitole vystačíme zpočátku ještě s projektem z minulé kapitoly, který budeme v případě potřeby doplňovat třídami, jež převezmeme z projektu 03_Třídy_Z.

4.1 Jednoduché vstupy a výstupy V minulé kapitole jsme si pověděli o literálech, avšak jejich použití jsme si moc nevyzkoušeli. Hned to napravíme. Při té příležitosti si zároveň ukážeme, jak mů-žete jednoduše uživateli něco sdělit nebo se jej na něco zeptat.

Page 177: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 177

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 177 z 433

Doplnění projektu o třídu odjinud Metody, o kterých budu za chvíli hovořit, jsou ve třídě VstupVýstup, která je součástí projektu 03_Třídy_Z. Než s nimi začneme pracovat, ukážu vám, jak můžete doplnit svůj projekt třídou z jiného projektu nebo jakéhokoliv jinéhopro vás dosažitelného místa (z disku, sítě, …).

1. V okně projektu zadejte příkaz Úpravy → Nová třída ze souboru….

2. BlueJ otevře dialogové okno pro zadání souboru. Najděte požadovanoutřídu, klepněte na ni a stiskněte Přidat.

3. BlueJ přidá zadanou třídu do vašeho projektu.

Jak prosté!

Textové řetězce Když jsem vás v podkapitole Datové typy na straně 52 poprvé seznamoval s dato-vým typem String, říkal jsem vám, že je to nejpoužívanější objektový datový typ. Protože textové řetězce budeme používat i při práci s ostatními typy literálů, tak s nimi náš výklad začneme.

Každý objekt je převeditelný na řetězec. Má k tomu vyhrazenu speciální me-todu toString(). Která třída nemá definovanou vlastní podobu této metody, té dodá potřebnou metodu třída Object.

V pasáži o textových literálech jsem vám ukazoval, jak je možno s využitím operátoru + vytvořit dlouhý řetězec z několika řetězců menších. Tyto menší řetěz-ce však nemusí být textové literály, mohou to být jakékoliv řetězce. Java jde do-konce tak daleko, že vám umožní přidat k řetězci cokoliv. Zjistí-li, že k řetězci „přičítáte“ něco jiného než řetězec, tak to prostě na řetězec převede.

Řeknu to trochu přesněji: Bude-li v součtu kterýkoliv ze sčítanců řetězec, převede se druhý sčítanec také na řetězec a výsledkem takovéhoto řetězcového součtu bude řetězec vzniklý spojením sčítanců.

☺ Uvedeného pravidla se často využívá k převodu hodnot nejrůznějších typů na řetězec. Máte-li např. metodu, která vyžaduje jako svůj para-metr řetězec, a chcete jí předat hodnotu jiného typu, stačí ji předat vý-raz ""+hodnota. Překladač najde součet prázdného řetězce s hodnotou a podle předchozího pravidla nejprve převede hodnotu na řetězec a pakoba řetězce sečte. Protože ale přičtením prázdného řetězce nic nezmě-níme, bude výsledkem hodnota převedená na textový řetězec.

Page 178: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

178 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 178 z 433

Zkusíme si malý experiment. Otevřete importovanou třídu VstupVýstup a najděte v ní následující statickou metodu: 1 2 3 4 5 6 7 8 9

10 11

public class VstupVýstup { public static String jméno() { String křestní = P.zadej( "Zadej své křestní jméno:", "Pepa" ); String příjmení = P.zadej( "Zadej své příjmení:", "" ); String jméno = křestní + " " + příjmení; P.zpráva( "Jmenuješ se:\n\n" + jméno ); return jméno; } }

Projděme si nyní tuto metodu příkaz za příkazem. Na pátém řádku je zavolána statická metoda třídy P, která otevře dialogové

okno, v němž zadáte požadovanou hodnotu. První parametr této metody je vý-zva, kterou vám program oznamuje, co vlastně máte zadat. Druhým parametrem je pak počáteční hodnota, kterou vám program ve vstupním poli nabídne. Po pro-vedení tohoto příkazu se tedy na obrazovce objeví dialogové okno z obr. 4.1.

Obrázek 4.1 Dialogové okno vyvolané příkazem z pátého řádku

Po zadání požadované hodnoty (tj. vašeho křestního jména) metoda vrátí zadaný řetězec a program jej uloží do proměnné křestní (pořád jsme na řádku 5).

Na dalším řádku vás program požádá o zadání vašeho příjmení. Oproti předchozímu příkazu se však již nepokouší o žádný odhad toho, jak byste se mohli jmenovat, a jako počáteční hodnotu ve vstupním poli proto zadává prázd-ný řetězec. Zadanou hodnotu od metody zadej převezme a uloží ji do proměnné příjmení.

Na sedmém řádku program složí obě části vašeho jména dohromady. Všim-něte si, že mezi ně bystře vkládá mezeru, protože jinak by na sebe byly nalepené (můžete si to vyzkoušet). Výsledný textový řetězec uloží do proměnné jméno.

Osmý řádek vám v dialogovém okně prozradí, jaké jméno jste právě zadali. Pokud bych tedy správně zadal svoje jméno, otevřelo by se okno z obr. 4.2.

Page 179: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 179

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 179 z 433

Obrázek 4.2 Počítač mi prozradil, co jsem zadal

Poslední řádek metody jméno vrací vytvořený řetězec tomu, kdo metodu zavolal. Protože jsem metodu zavolal přímo z prostředí BlueJ, vrátí metoda hodnotu vola-jícímu prostředí. Jak víme, BlueJ po zavolání metod, jež něco vracejí, otevře dialo-gové okno, v němž nám prozradí, co že jim volaná metoda vrátila (viz obr. 4.3).

Obrázek 4.3 BlueJ vám prozradí, co mu metoda vrátila

Rozdíl mezi prázdným řetězcem a null V souvislosti s textovými řetězci bych vás chtěl upozornit ještě na jednu drobnost, která dělává začátečníkům potíže. Jde o to, že si občas neuvědomují, že prázdný řetězec a žádný řetězec jsou dvě různé věci.

Prázdný řetězec, tj. řetězec "", je řetězec, který neobsahuje žádný znak. Pořád je to ale platný řetězec, jenom v něm nic není. Uložíme-li proto do proměnné od-kaz na prázdný řetězec, tak proměnná opravdu někam odkazuje. Odkazuje na ně-co, co existuje.

Naproti tomu vložíme-li do řetězcové proměnné null, říkáme tím, že pro-měnná v danou chvíli nikam neukazuje, takže nemá smysl se o dokazované in-stanci bavit a už vůbec nemá smysl se jí pokoušet použít.

A teď pozor, přijde myšlenkový přemet: I když nemá smysl se bavit o instan-ci, na níž taková proměnná odkazuje (když přece nikam neodkazuje), má smysl se bavit o tom, kam proměnná odkazuje – přece nikam. To je informace, kterou často potřebujeme vědět – např. proto, abychom věděli, můžeme-li proměnnou někde použít, nebo musíme-li jí nejprve přiřadit odkaz na nějakou existující instanci.

Rozdíl můžete zjistit i při spuštění metody jméno, kterou jsme před chvílí na-programovali. Nezadáte-li nic a stisknete OK, vrátí metoda zadej odkaz na prázd-

Page 180: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

180 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 180 z 433

ný řetězec (zadali jste řetězec, v němž není žádný znak). Stisknete-li však Cancel nebo zavřete-li okno tlačítkem na titulkové liště či stiskem ESC, vrátí prázdný ukazatel (null), protože jste nic nezadali (na obsahu textového pole v okamžiku zavření okna v takovém případě nezáleží). Podle toho vypadá i text, který vám metoda vypíše v následující zprávě.

Čísla Tak jsme si pohráli s textovými řetězci a můžeme si jít hrát s čísly. Zkuste do třídy VstupVýstup přidat takovou malou děličku. Rozdíl oproti minulému programu bude v tom, že uživatel nebude zadávat jména, ale čísla, která je třeba vydělit, a ve výsledném okně se pak dozví výsledek. (Vzorové řešení za chvilku uvedu.)

Protože se ale výsledek dělení liší v závislosti na typu svých argumentů, na-programujte dělící metody hned dvě: první nazvěte celočíselnéDělení a druhou reálnéDělení. Využijte přitom toho, že metoda zadej má tři přetížené verze, které se liší typem zadávané implicitní hodnoty. Zadáte-li jako implicitní hodnotu celé číslo, vrátí vám také celé číslo (zadá-li uživatel nějaký nesmysl, který není možno interpretovat jako celé číslo, tak se vzbouří). Zdáte-li jako implicitní hodnotu číslo typu double, vrátí vám zadanou hodnotou jako hodnotu typu double, a to i tehdy, když uživatel zadal celé číslo.

Zkuste v obou metodách zjistit nejenom podíl, ale také zbytek po dělení. Ten sice nebudete moci vrátit jako funkční hodnotu, protože Java umožňuje vrátit pouze jednu věc, ale můžete jej alespoň vytisknout v okně s výsledkem dělení.

Protože jste už velice podobnou metodu viděli, mohli byste se o její napro-gramování pokusit sami. Až budete se svým dílem hotovi, vyzkoušejte ji a pak se podívejte na následující program a porovnejte, co jste dělali jinak. 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18

public static int celočíselnéDělení() { int dělenec = P.zadej( "Zadej dělence (celé číslo):", 0 ); int dělitel = P.zadej( "Zadej dělitele (celé číslo):", 1 ); int podíl = dělenec / dělitel; int zbytek = dělenec % dělitel; P.zpráva( dělenec + " : " + dělitel + " = " + podíl + ", zbyde " + zbytek ); return podíl; } public static double reálnéDělení() { double dělenec = P.zadej( "Zadej dělence:", 0 ); double dělitel = P.zadej( "Zadej dělitele:", 1 ); double podíl = dělenec / dělitel; double zbytek = dělenec % dělitel;

Page 181: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 181

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 181 z 433

19 20 21 22

P.zpráva( dělenec + " : " + dělitel + " = " + podíl + "\n\"zbytek\" = " + zbytek ); return podíl; }

Jak vám předchozí program prozradil, operátor získání zbytku po dělení je defi-nován i pro reálná čísla. Nebudu vám tu ale vysvětlovat, jako pracuje. Zkuste s ním zaexperimentovat a přijděte na to sami.

4.2 Knihovny statických metod V některých případech vytváříme třídu ne jako „továrnu na instance“, ale jako úložiště často používaných metod. Jsou to vlastně takové knihovny. Typickým příkladem byla třída VstupVýstup, se kterou jsme si hráli před chvílí nebo třída P, s níž pracujeme již od počátku třetí kapitoly.

Metody v těchto třídách bývají definovány výhradně jako statické a vše, co potřebují, jim musíme předat v jejich parametrech. Pouze ve výjimečných přípa-dech uchovávají některé sdílené informace v atributech třídy – např. hodnoty klí-čových konstant (třeba číslo π).

Když víme, že instanci takovéto třídy nebudeme potřebovat (její metody si vystačí samy), bylo by vhodné zařídit, aby tato instance ani vytvořit nešla. Toho dosáhneme jednoduše: definujeme soukromý bezparametrický konstruktor. (Teo-reticky bychom mohli definovat i parametrický, ale žádného zlepšení bychom tím nedosáhli a jenom bychom to zbytečně zkomplikovali.)

Definicí soukromého konstruktoru zabezpečíme, že nikdo nebude moci vy-robit instanci této třídy. Protože je konstruktor soukromý, nikdo k němu nemůže, a protože je konstruktor již definován, nestane se, že by překladač za nás vyrobil konstruktor implicitní.

Definice soukromého bezparametrického konstruktoru zamezujícího tvorbu instancí je proto použita i ve třídě VstupVýstup.

4.3 Podrobnosti o operátorech Na chvilku si odpočineme od našeho stromu a rozšiřování jeho vlastností a poví-me si něco o možnostech, které nám Java nabízí pro počítání. Doposud jsme se se-tkali s devíti operátory (ani jste si nevšimli, co?). Zopakujeme si nyní jejich

Page 182: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

182 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 182 z 433

význam a doplňme si naše znalosti o některé jejich další vlastnosti. Pak se sezná-míme s novou skupinou operátorů.

Než se rozpovídám o operátorech, připomenu vám nejprve několiktermínů, které budu vzápětí používat:

Operace je to, co se provede. Operace sčítání sečte dvě čísla, operacepřiřazení přiřadí proměnné nějakou hodnotu, operace vlože-ní do závorek zařídí, že se uzavřený výraz nejprve spočte a teprve pak použije, atd.

Operátor je znak nebo skupina znaků, které oznamují, jaká operace se má v daném místě provést. Chceme-li dvě čísla sečíst, vloží-me mezi ně operátor +, chceme-li zařídit, aby se část výrazu spočetla přednostně, vložíme před ní a za ní odpovídající ku-laté závorky.

Jak ukazují právě závorky, mezi operátory existují i ta-kové, které jsou tvořeny několika znaky, z nichž každý stojíve výrazu někde jinde.

Operand je výraz, s nímž se provádí operace. Sčítáme-li dvě hodnoty, označujeme je jako operandy. Uzavíráme-li část výrazu do závorek, je tato část operandem závorek.

Arita Tak tohle slovo asi většina z vás ještě neslyšela. Arita operaceříká, kolik v ní vystupuje operandů. Rozeznáváme operaceunární (s jedním operandem – např. závorky), binární (se dvěma operandy – např. sčítání) a ternární (se třemi operan-dy – taková je v Javě jenom jedna a setkáme se s ní, až bu-deme učit počítač přemýšlet).

Binární aritmetické operátory + – * / %

Sčítání, odčítání, násobení První tři z operátorů uvedených v nadpisu, tj. operátor sčítání (+), odčítání (–) a násobení (*), jsou průzračné a nedělají nikomu potíže. Každý z nich spočte hodno-ty výrazů vpravo a vlevo od operátoru a s výsledky provede požadovanou opera-ci.

Page 183: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 183

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 183 z 433

Jediné, na co nesmíte při jejich používání zapomenout je, že v programování má, stejně jako v matematice, násobení přednost před sčítáním. Potřebujete-li pro-to nejprve dvě hodnoty sečíst a teprve pak vynásobit, máte dvě možnosti:

První možnost znáte z matematiky: sčítané hodnoty uzavřete do závorek a výsledný součet vynásobíte – např. (3 + 4) * (5 + 6).

Druhá možnost je trochu víc programátorská – celou složitou operaci prostě rozdělíte do několika kroků:

int a = 3 + 4; int b = 5 + 6; int c = a * b;

Většinou dáte přednost závorkám, nicméně existují situace, kdy je výhodnější celý výpočet rozdělit do několika kroků.

Možná to některé překvapí, ale výsledný program je v obou případech prakticky stejný. Podíváte-li se totiž do přeloženého programu, zjistíte, že nerozdělíte-li výpočet do několika kroků vy, udělá to za vás překla-dač. Dávejte proto vždy přednost takovému zápisu, který je pro váspřehlednější.

Slučování řetězců + Jak již víme, operátor + je možno použít nejenom jako operátor sčítání v aritmetic-kých výrazech, ale také jako operátor slučování řetězců. Řekli jsme si, že jakmile je některý z operandů operátoru + řetězec, převede se na řetězec i druhý operand a výsledkem je řetězec vzniklý jejich spojením.

Někdy ale potřebujeme ve slučovaném řetězci napřed něco sečíst – pak mu-síme tento součet uzavřít do závorek, aby se nejprve sečetl a teprve výsledek pře-vedl na řetězec a použil při slučování. Budeme-li tedy chtít zobrazit výsledek násobení čísel a a b, budeme zobrazovaný řetězec generovat např. následovně:

String násobilka = a + " * " + b + " = " + (a*b);

Dělení / Možná se divíte, proč uvádím dělení zvlášť, vždyť je to stejná operace jako před-chozí tři. Je a není. Na dělení je zajímavé to, že výsledek závisí na tom, jakého ty-pu jsou operandy.

Page 184: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

184 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 184 z 433

Je-li některý z operandů reálné číslo (např. typu double), je výsledkem opět reálné číslo, a to „přesný“ podíl – např. 2.0/4 je 0.5 (připomínám, že v pro-gramování se místo desetinné čárky píše desetinná tečka).

Jsou-li oba operandy celočíselné, je výsledkem celočíselný podíl, tj. to, co zbude z podílu po odříznutí jeho desetinné části. Zůstaneme-li u příkladu z předchozího odstavce, pak platí, že 2/4 je 0 (nula), 12/5 je 2 a -12/5 je -2.

Chcete-li si vyzkoušet, jak celočíselné dělení pracuje, zkopírujte z pro-jektu 03_Třídy_Z třídu Počty a spusťte její statickou metodu dělení(). Vý-stupní okno ukazuje obr. 4.4.

Obrázek 4.4 Výsledky dělení

Metoda dělení() má „zadrátovaný“ jediný příklad (pak se dá jednoduše dosáh-nout pěkného zarovnání pod sebou). Vedle toho existuje v nabídce i metoda dělení(double,double), která vám umožní zadat dvě čísla, jejichž podíly budou zobrazeny v dialogovém okně (její výsledky už tak pěkně zarovnané být nemusí). Zkuste si s ní trochu zaexperimentovat.

Zbytek po dělení (dělení modulo) % Dělení modulo vrátí zbytek po dělení výrazu vlevo výrazem vpravo. Výsledky se tentokrát počítají pro reálná i celá čísla stejně: odčítáte dělitele od dělence (jmeno-vatele od čitatele) tak dlouho, až vám zbude tak malé číslo, že byste při příštím odčítání ʺpřeběhli přes nuluʺ (takto je operace popsána v definici jazyka). Z toho zákonitě plyne, že znaménko výsledku respektuje znaménko dělence.

Page 185: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 185

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 185 z 433

Obrázek 4.5 Výsledky dělení modulo

I s dělením modulo můžete zaexperimentovat. Pro tento účel je ve třídě Počty při-pravena metoda modulo(double,double), které zadáte dělence a dělitele a ona vám vrátí sadu výsledků operace.

Unární operátory + – Operátory + a – mohou vystupovat v několika rolích. U operátoru + jsme si již ukazovali, že podle typu operandů je to jednou operátor sčítání a podruhé operá-tor slučování řetězců. Nyní si ukážeme jeho třetí podobu.

Oba operátory mohou pracovat také jako unární operátory, které pouze ovlivňují znaménko svého (jediného) operandu. Ve škole vás učili, že je jedno, napíšeme-li 5 nebo +5 a učili vás také o významu osamoceného znaménka – před číslem nebo před výrazem – např. -5 nebo –(a + b).

Java v tomto směru nepřináší nic nového. Jediné, na co musíte dávat pozor, je to, abyste náhodou nenapsali dva stejné operátory těsně vedle sebe (tj. bez vlože-né mezery), protože to by překladač mohl pochopit jako něco úplně jiného (blíže se s touto možností seznámíme v kapitole Inkrementační a dekrementační operátory na straně 191).

Kulaté závorky ( ) Jak jsem již řekl, závorky jsou v programování chápány také jako operátor, a to ja-ko operátor, který má přednost před všemi ostatními. Jako každý operátor i zá-vorky vracejí hodnotu, a to hodnotu výrazu, který je v nich uzavřen.

Použití závorek snad nemusím vysvětlovat. Je stejné jako v matematice. Jedi-ným rozdílem je to, že v matematice zpřehledňujeme výrazy používáním několika různých druhů závorek, kdežto v programování používáme k označení toho, co se má spočítat jako první, vždy pouze kulaté závorky. Ostatní druhy závorek slouží k jiným účelům.

Page 186: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

186 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 186 z 433

V programovacích jazycích se používá velké množství operátorů. Každý z nich má definovanou tzv. prioritu, podle které se pozná, kdo má před kým před-nost (víte například, že násobení má přednost před sčítáním). Jen málo programá-torů si dokáže pamatovat celou posloupnost priorit. Nebudu k tomu nutit ani vás, protože vždy existuje jednoduchá pomoc: nevíte-li, co se bude dělat dřív, použij-te závorky.

Někteří programátoři se za používání závorek asi stydí, protože je dávají opravdu pouze tam, kde to bez nich nejde. Myslete ale na to, že je lepší mít pro-gram se závorkami, ale bez chyb, než naopak. Kromě toho myslete i na nešťastní-ky, kteří budou číst program po vás a nebudou mít třeba tak dokonalé znalosti toho, kdo má před kým přednost, a mohli by proto váš program pochopit špatně.

Přiřazovací operátor = Operátor přiřazení spočte hodnotu výrazu, který je vpravo od něj a uloží ji do proměnné, která je vlevo od něj – např. ve výrazu

počet = počet + 1

se nejprve spočte, kolik je počet+1 a výsledek se pak uloží jako nová hodnota do proměnné počet (výsledkem je tedy zvýšení hodnoty proměnné počet o jedničku).

Operace přiřazení však nepředstavuje pouze vlastní akci spočtení a uložení nové hodnoty. I ona, stejně jako např. aritmetické operace, vrací hodnotu. Touto vracenou hodnotou je přiřazovaná hodnota. Na vyzkoušení jsem vám opět při-pravil jednoduchou metodu, která testuje pravdivost tvrzení, že

(a + b) * (a - b) = a*a – b*b. 1 2 3 4 5 6 7 8 9

10

public static void přiřazení( int a, int b ) { int a2, b2, součet, rozdíl, součin; String s1 = "Čísla: a=" + a + ", b=" + b; String s2 = "\nMocniny: a2=" + (a2=a*a) + ", b2=" + (b2=b*b); String s3 = "\nsoučet=" + (součet=a+b) + ", rozdíl=" + (rozdíl=a-b); String s4 = "\nRozdíl mocnin (a2 - b2): " + (a2 - b2); String s5 = "\nSoučin (a+b)*(a-b): " + (součet*rozdíl); P.zpráva( s1 + s2 + s3 + s4 + s5 ); }

Používat ve výrazech hodnoty vrácené operací přiřazení tak, jak to dělá metoda přiřazení(int,int) se moc nedoporučuje, protože to většinou znepřehledňuje program. Jsou ale situace, kdy takovou možnost přivítáte.

Page 187: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 187

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 187 z 433

Sdružené přiřazovací operátory +=, –=, *=, /=, %= Java umožňuje sloučit kteroukoliv z aritmetických operací s operátorem přiřazení a zrychlit tak celý výpočet. Princip je jednoduchý – budu-li místo příslušného aritmetického operátoru + - * / % psát znak #, pak výraz

operand_1 #= operand_2;

bude naprosto ekvivalentní výrazu

operand_1 = operand_1 # (operand_2);

Všimněte si, že v posledním výrazu je operand_2 uzavřen do závorek. Je to proto, že operandem může být i výraz a operátor použitý ve výrazu by se nám mohl „pohádat s operátorem + o to, kdo bude mít přednost. Vezměte si např. výraz

a *= b + c;

Pokud bychom nechtěli použít složený přiřazovací operátor, museli bychom tento výraz přepsat do tvaru:

a = a * (b + c);

protože kdybychom nepoužili závorky, tak by se napřed vynásobilo a*b a teprve pak by se přečetlo c. Složený přiřazovací příkaz za vás takovéto dilema jednoduše vyřeší.

Aby se vám sdružené operátory dostaly alespoň trochu pod kůži (v progra-mech totiž na ně narazíte velmi často), připravil jsem jednoduchou metodu sdružené(int,int), s jejíž pomocí si můžete zase vše vyzkoušet: 1 2 3 4 5 6 7 8 9

10 11 12 13 14

public static void sdružené ( int a, int b ) { String s0 = "Start: a=" + a + ", b=" + b; a += a + b; b += b; String s1 = "\nPrvní: a=" + a + ", b=" + b; a += a + b; b += b; String s2 = "\nDruhá: a=" + a + ", b=" + b; a += a + b; b += b; String s3 = "\nTřetí: a=" + a + ", b=" + b; P.zpráva( s0 + s1 + s2 + s3 ); }

Zkoušejte volat metodu sdružené(int,int) s různými hodnotami parametrů a vy-zkoušejte si i změnu sdruženého operátoru – použijte např. místo operátoru += operátor *=.

Page 188: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

188 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 188 z 433

Operátor přetypován (typ) Jsou okamžiky, kdy bychom potřebovali změnit typ nějakého výrazu. Někdy je to proto, že nám aktuální typ výrazu nevyhovuje a změna jeho typu by pomohla, jindy je to proto, že víme něco, co překladač neví, a můžeme mu tímto způsobem napovědět (s takovouto situací se setkáme v 6. kapitole).

V pasáži o operátoru dělení jsme si například říkali, že výsledek dělení závisí na typech operandů. Jsou-li oba celočíselné, je výsledkem celá část podílu, je-li alespoň jeden z nich reálný, je výsledkem reálné číslo reprezentující přesný podíl.

Nyní si představte, že máme celá čísla a a b a potřebujeme spočítat přesný podíl výrazu (a-b)/(a+b). Protože součet i rozdíl celých čísel je celé číslo, budeme dělit dvě celá čísla a budou-li obě čísla kladná, bude výsledek vždy nula, protože čitatel bude vždy menší než jmenovatel.

Budeme-li požadovat přesný podíl, nemůžeme čísla jen tak podělit, ale mu-síme si nějak pomoci. Máme v podstatě dvě možnosti:

Součet a/nebo rozdíl uložíme do reálné proměnné, takže v podílu bude ale-spoň jeden operátor reálný a získáme tak reálný i výsledek – např. public double podíl( int a, int b ) { double součet = a + b; double rozdíl = a – b; return rozdíl / součet; }

Jeden z operandů přetypujeme (můžeme klidně i oba) na reálné číslo, takže budeme dělit např. reálné číslo celým. Jak víme, v takovém případě překla-dač převede na reálné číslo i druhý operand a vrátí požadovaný „přesný po-díl“ – např.: public double podíl( int a, int b ) { return (double)(a - b) / (a + b); }

Jak jste mohli vidět v předchozí ukázce, k přetypování se používá název cílového typu uzavřený v kulatých závorkách. To celé, tj. závorka-typ-závorka, pak ozna-čujeme jako operátor přetypování.

Před každým takovýmto přetypováním si počítač nejprve zkontroluje, jestli je povolené. Pokud bychom např. chtěli přetypovat logickou hodnotu na číslo, se zlou bychom se potázali. Obdobně bychom nepochodili ani v případě, že bychom chtěli na číslo přetypovat instanci nějakého objektového typu.

Z typů, které jsme probrali, můžeme prozatím navzájem přetypovávat pouze číselné typy a znakový typ:

Page 189: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 189

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 189 z 433

Přetypováním celého čísla na reálné získáme reálné číslo s hodnotou přety-povávaného celého čísla.

Přetypováním reálného čísla na celé dostaneme číslo s useknutou desetinnou částí.

Přetypováním znaku na číslo získáme kód daného znaku.

Přetypováním čísla na znak získáme znak s daným kódem.

Překladač si vždy zkontroluje, jestli je příslušné přetypování povoleno. Budete-li chtít např. přetypovat logickou hodnotu nebo instanci objek-tového typu na celé číslo, překladač vám to nepovolí.

Pseudopřetypování na String Kromě výše uvedených „řádných“ přetypování bych tu přidal ještě jedno „faleš-né“. Je jím přetypování čehokoliv na řetězce (String). „Řádné“ přetypování na ře-tězce pomocí operátoru (String) vám totiž překladač většinou nepovolí. Co ale projde vždy, je výraz ""+x, kde x může být proměnná libovolného typu, objekto-vého i primitivního (již jsem se o tom zmiňoval v poznámce na straně 177).

Důvod je jednoduchý – nejedná se o přetypování, ale o sloučení řetězců. Proto-že na levé straně operátoru + stojí řetězce, převede počítač i pravý operand na ře-tězec (u instancí objektových typů zavolá jejich metodu toString() a dosadí vrácený výsledek) a k výsledku přidá prázdný řetězec, tj. nic. Místo původního x (přitom x nemusí být jen proměnná, ale může to být i výraz, který je nejdříve po-třeba spočítat) tak získáme řetězec, který je tou nejlepší řetězcovou reprezentací x.

Uvedený postup je sice univerzální a vždy funguje, ale není nejrychlej-ší, protože se při něm dělá několik zbytečných operací. Chcete-li ušetřit nějakou tu nanosekundu, musíte u objektových typů zavolat přímo me-todu toString() a u primitivních typů statickou metodu valueOf jejich obalového typu, což je objektový typ, který zastupuje daný primitivní typ v situacích, ve kterých se primitivní typy používat nemohou (podrob-něji si o nich povíme v kapitole Kontejnery nejsou jen na odpadky na stra-ně 402). Prozatím vám prozradím, že pro převod

celých čísel slouží metoda Integer.valueOf(int),

reálných čísel slouží metoda Double.valueOf(int),

logických hodnot slouží metoda Boolean.valueOf(int).

Page 190: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

190 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 190 z 433

4.4 Počítáme instance Zanechme nyní na chvíli teorie a pojďme se opět podívat, jak bychom mohli dále zdokonalit naši geniální třídu kreslící strom.

V řadě situací bývá výhodné počítat vytvořené instance a přidělit každé vy-tvořené instanci její „rodné číslo“, do kterého uložíme pořadí vytvořené instance. Toto rodné číslo bude pro danou instanci konstantní. Dostane je „při narození“ a udrží si je až do smrti. Bude ji odlišovat od všech ostatních instancí dané třídy.

Podívejme se, jak bychom takovouto úlohu vyřešili pro náš strom. Nejprve musíme vymyslet, jak poznáme, kolikátou instanci vytváříme. K tomu může slou-žit atribut třídy, který bude mít na počátku nulovou hodnotu a při vytváření nové instance k němu vždy připočteme jedničku. Nazvěme tento atribut počet, protože v něm bude neustále uchováván počet vytvořených instancí dané třídy.

Rodné číslo instance můžeme uchovávat v jiném atributu, do kterého při vy-tváření instance uložíme aktuální počet vytvořených instancí, který bude součas-ně pořadovým číslem vzniku dané instance. Protože tento atribut označuje pořadí vzniku dané instance, nezveme jej pořadí, a protože se jeho hodnota nebude v průběhu života instance měnit, definujme jej jako konstantu.

Abychom mohli toto rodné číslo co nejlépe využít, definujeme ještě konstant-ní „stringový“ atribut název, jehož obsahem bude názve třídy následovaný podtr-žítkem a rodným číslem. Tento atribut doplníme přístupovou metodou getNázev(), která nám název instance na požádání vrátí.

Část těla třídy Strom, která tuto úlohu řeší, by mohla být naprogramovaná např. následovně: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21

public class Strom { // ... /** Počet doposud vytvořených instancí. */ private static int počet = 0; /** Pořadí kolikátá byla daná instance vytvořena v rámci třídy. */ private final int pořadí; /** Název sestávající z názvu třídy a pořadí instance */ private final String název = "Strom_" + pořadí; /*************************************************************************** * Vytvoří na zadaných souřadnicích instanci se zadanou šířkou, výškou * a poměrem velikosti kmene ku zbytku stromu. * Vytvořené instanci přiřadí její "rodné číslo". */ public Strom( int x, int y, int šířka, int výška, int podílŠířkyKmene, int podílVýškyKmene )

Page 191: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 191

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 191 z 433

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

{ počet += 1; //Počet vytvořených instancí bude o jednu vyšší this.pořadí = počet; //Pořadové číslo vytvářené instance this.název = "Strom_" + pořadí; int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene; int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = ( šířka - šířkaKmene) / 2; koruna = new Elipsa ( x, y, šířka, výškaKoruny, Barva.ZELENÁ ); kmen = new Obdélník( x+posunKmene, y+výškaKoruny, šířkaKmene, výškaKmene, Barva.ČERVENÁ ); } // ... }

Vlastní přiřazení rodného čísla instance probíhá na řádcích 23 a 24. Na řádku 23 zvětšujeme hodnotu uloženou v atributu počet o jedničku a v řádku 24 pak tuto novou hodnotu přiřazujeme do proměnné pořadí jako rodné číslo dané instance. Na řádku 25 pak přiřazujeme hodnotu konstantě název.

Doplňte podobným počítáním instancí i svoji vlastní třídu z minulé ka-pitoly.

4.5 Inkrementační a dekrementační operátory

Velmi často potřebujeme v programu zvětšit nebo zmenšit hodnotu nějaké číselné proměnné o jedničku. Tato operace je tak častou, že se jí autoři jazyka rozhodli věnovat vlastní operátory. Pro přičtení jedničky (inkrementaci) používáme operá-tor ++ a pro její odečtení (dekrementaci) operátor --.

Tyto operátory pracují pouze s jediným operandem – s tím, který mají zvětšit nebo zmenšit. Nazýváme je proto unární, na rozdíl od operátorů +, -, * a /, které pracují se dvěma operandy a označujeme je proto jako binární.

Operátory ++ a -- mají jednu zvláštnost: můžeme si vybrat, umístíme-li je před anebo za upravovaný operand. V prvním případě pak operátory označujeme jako

Page 192: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

192 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 192 z 433

preinkrementační, resp. predekrementační, v druhém jako postinkrementační, resp. postdekrementační.

Je-li operace jedinou operací v příkazu, tak se rozdíl v umístění operátoru ni-jak neprojeví. Je-li však tato operace součástí složitějšího výrazu, pak se u prein-krementačního operátoru nejprve přičte k proměnné jednička a ve výrazu se použije zvětšená proměnná, kdežto u postinkrementačního operátoru se proměn-ná nejprve použije ve výrazu a teprve pak se o jedničku zvětší. Úplně stejně je to i s dekrementací.

Ukážeme si to na příkladech. V následující tabulce jsou v širokém levém sloupci uvedeny příkazy a ve třech úzkých sloupcích hodnoty proměnných po provedení příkazu vlevo.

Příkaz i j kint i=1, j=0, k=0; 1 0 0 j = i++; 2 1 0 k = ++i 3 1 3 k = i-- + j--; 2 0 4 k = --i + --j; 1 -1 0

Zkuste si příkazy z levého sloupce provést nejprve sami a pak své výsledky po-rovnejte s hodnotami ve sloupcích vpravo. Pro ty, jejichž výsledky se budou lišit, uvádím pro jistotu malé vysvětlení:

1. V prvním řádku pouze přidělíme počáteční hodnoty, takže vpravo najdeme vypsané hodnoty, které jsme vlevo přiřadili.

2. Ve druhém řádku nejprve použijeme současnou hodnotu i (=1), tu přiřadíme do j, a pak hodnotu i zvětšíme o jedničku.

3. Ve třetím řádku naopak nejprve zvětšíme i, takže bude mít hodnotu 3, a tuto novou hodnotu přiřadíme do proměnné k.

4. Ve čtvrtém řádku sečteme současné hodnoty proměnných i (=3) a j (=1), vý-sledek uložíme do k. Hodnoty proměnných i a j po použití ještě zmenšíme o jedničku.

5. V posledním pátém řádku nejprve zmenšíme hodnoty proměnných i a j a ty-to změněné hodnoty pak sečteme a výsledek přiřadíme do k.

Page 193: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 193

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 193 z 433

Definujte třídu Xkrementy a v ní statickou metodu test(), ve které ověří-te, že inkrementační a dekrementační operátory fungují tak, jak popisu-je tabulka. Přiřaďte vždy proměnným nové hodnoty a řetězec s provedenou operací a výslednými hodnotami zobrazte pomocí metody P.zprava(String). Nezapomeňte doplnit zdrojový kód o komentáře.

Abyste to ale neměli tak jednoduché, tak vám to trochu zkomplikuji. Jak si možná vybavíte, již několikrát jsem říkal, že dobrý programátor neopisuje stejný kód. Popsané zadání si ale přímo koleduje o to, aby seopakovaně dělalo totéž.

Takové situace se většinou řeší pomocnou metodou, v níž napro-gramujeme opakovanou část programu a na potřebných místech pro-gramu pak tuto metodu pouze zavoláme.

Pro jistotu jenom upozorňuji, že když jsou metody s opakujícím se kódem statické, musí být statická i pomocná metoda. A když je pomoc-ná, tak by měla být také soukromá, protože nikomu není nic do toho,jaké pomocné metody ve třídě používám.

Těm činorodým pak přidám ještě doplňující úkol: skládejte postup-ně jednotlivé tištěné řetězce „na hromadu“ a na závěr je ještě jednou vy-tiskněte všechny najednou.

Doufám, že nebudete číst dál, dokud tento prográmek nevytvoříte. Je poměrně jednoduchý a chcete-li se opravdu naučit programovat, byla by ostuda, kdybyste se spokojili pouze s tím, že si prohlédnete následné vzorové řešení. Není určeno pro lenochy, ale pro ty, kteří knihu studují sami bez učitele a potřebují mít možnost si své programy s něčím porovnat. 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19

/******************************************************************************* * Třída má za úkol otestovat chování inkrementačních a dekrementačních * operátorů. */ public class Xkrementy { //i, j, k jsou definovány jako statické atributy, //aby je metoda test mohla sdílet s metodou zobraz private static int i, j, k; //Atribut vše slouží jako akumulátor vytvářeného řetězce private static String vše = ""; /*************************************************************************** * Metoda ověří, že se operátory chovají tak, * jak je v učebnici uvedeno v tabulce. */ public static void test()

Page 194: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

194 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 194 z 433

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

{ //Statické atributy jsou na počátku nulové => stačí nastavit i i = 1; zobraz( "Počáteční hodnoty"); j = i++; zobraz( "Po (j = i++)" ); k = ++i; zobraz( "Po (k = ++i)"); k = i-- + j--; zobraz( "Po (k = i-- + j--)"); k = --i + --j; zobraz( "Po (k = --i + --j)"); P.zpráva( vše ); } /*************************************************************************** * Pomocná metoda, která zobrazí poslední akci a * vytiskne pomocí metody P.zpráva() hodnoty všech číselných atributů. * Zaroveň přidá tištěný text do souhrnného řetězce, * který se bude tisknout na závěr. */ private static void zobraz( String text ) { text = text + ": i=" + i + ", j=" + j + ", k=" + k; P.zpráva( text ); vše = vše + "\n" + text; } }

Spustíte-li si metodu test z předchozího programu několikrát za sebou, zjistíte, že byla vlastně navržena jako metoda na jedno použití, protožepředpokládá, že při jejím spuštění jsou nastaveny správně počátečníhodnoty statických atributů. Po ukončení metody však mají tyto atribu-ty zcela jiné hodnoty, takže při příštím spuštění budou výsledky vypa-dat jinak. Pokud by nám takovéto chování nevyhovovalo a chtělibychom, aby se metoda při každém spuštění chovala stejně, museli by-chom na jejím počátku požadovaným způsobem inicializovat všechnyatributy, které používá.

Odbočka o obecných zásadách programování Nyní na chvilku odbočím od hlavního proudu výkladu a povím vám něco o obecných zásadách tvorby programů.

Problémy, ně něž upozornila předchozí poznámka, jsou důsledkem rozhod-nutí předávat si hodnoty mezi dvěma metodami prostřednictvím atributů. Před-chozí program tak ušetřil programátorovi trochu toho psaní. Nicméně obecně se používání atributů pro předávání hodnot mezi metodami nedoporučuje, protože

Page 195: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 195

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 195 z 433

narušuje zapouzdření – k těmto atributům totiž mají přístup i metody, které ne-mají s daným problémem nic společného.

Následující prográmek ukazuje, jak je možno stejný problém řešit bez pomoci atributů za pomoci lokálních proměnných, které zapouzdření nenarušují (aby-chom neřešili dvakrát úplně stejnou úlohu, tak jsem proměnné i, j a k nedefino-val jako lokální proměnné, ale jako parametry). 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

/*************************************************************************** * Metoda ověří, že se operátory chovají tak, jak je v učebnici uvedeno * v tabulce. Metoda umožnuje zadání výchozích hodnot proměnných * a nepoužívá atributy, ale nahrazuje je lokálními proměnnými. */ public static void test( int i, int j, int k ) { String vše; i = 1; vše = zobraz( i, j, k, "Počáteční hodnoty"); j = i++; vše += '\n' + zobraz( i, j, k, "Po (j = i++)" ); k = ++i; vše += '\n' + zobraz( i, j, k, "Po (k = ++i)" ); k = i-- + j--; vše += '\n' + zobraz( i, j, k, "Po (k = i-- + j--)" ); k = --i + --j; vše += '\n' + zobraz( i, j, k, "Po (k = --i + --j)" ); P.zpráva( vše ); } /*************************************************************************** * Pomocná metoda, která zobrazí poslední akci a * vytiskne pomocí metody P.zpráva() hodnoty všech číselných parametrů. * * @return Vytištěný řetězec. */ private static String zobraz( int i, int j, int k, String text ) { text = text + ": i=" + i + ", j=" + j + ", k=" + k; P.zpráva( text ); return text; }

Jak vidíte, řešení je poněkud komplikovanější a pro mnohé asi také méně pře-hledné. Plyne z toho jedna rada: hlavní zásadou programátora by mělo být, aby jeho programy byly funkční a co nejpřehlednější. Bude-li po porušení některé z obecných zásad program přehlednější a čitelnější, neostýchejte se tuto zásadu porušit.

Obě verze řešení úlohy (tj. s parametry i bez nich) najdete ve tříděXkrementyRUP, kterou si můžete stáhnout z projektu 03_Třídy_Z.

Page 196: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

196 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 196 z 433

Jiný způsob inicializace rodného čísla S využitím operátoru preinkrementace bychom mohli inicializovat atribut pořadí již v deklaraci a vůbec bychom s tím nemuseli zatěžovat tělo konstruktoru. Nová podoba deklarace by pak měla tvar: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15

public class Strom { // ... Nezobrazená část programu /** Počet doposud vytvořených instancí. */ private static int počet = 0; /** Pořadí kolikátá byla daná instance vytvořena v rámci třídy. */ private final int pořadí = ++počet; /** Název sestávající z názvu třídy a pořadí instance */ private final String název = "Strom_" + pořadí; // ... Nezobrazená část programu }

V deklaraci na řádku 9 preinkrementační operátor nejprve zvětší o jedničku hod-notu uloženou v atributu počet a tato zvětšená hodnota se pak přiřadí atributu pořadí.

4.6 Standardní výstup Doposud jsme pro všechny naše výstupy používali statické metody pomocné tří-dy P, které zobrazili textový řetězec v dialogovém okně. Takovýto výstup sice vy-padá komfortně, avšak v některých situacích není optimální.

Příkladem situace, kdy se nám postupný výstup do oken zrovna nehodil, by-la poslední úloha, kdy jsem po vás chtěl, abyste na závěr vytiskli všechny příkla-dy pěkně pohromadě. Ve vzorovém programu jsem vám sice ukázal, jak byste si mohli poradit, ale existovala i jiná řešení.

Jedním z nich je postupné posílání vystupujících řetězců na standardní vý-stup. K tomu slouží metoda print objektu out, který je veřejným statickým atribu-tem třídy System. Tato metoda požaduje jeden parametr, který však může být libovolného typu. Tento parametr převede na řetězec a řetězec pošle na standard-ní výstup.

Protože většina požadavků tisku na standardní výstup končí odřádkováním, nabízí objekt out ještě metodu println, která po vytištění řetězce sama odřádkuje.

Page 197: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 197

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 197 z 433

Ukažme si použití těchto metod opět na příkladu. Doplníme předchozí třídu o statickou metodu ttest (tištěný test), která bude řešit stejný problém, ale místo pomocné metody zobraz(String) použije pomocnou metodu tiskni(String), kte-rá vytiskne zadaný řetězec na standardní výstup. Abych jenom neopisoval dřívěj-ší řešení, upravil jsem výstup tak, aby metoda ttest byla ještě stručnější: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21 22 23

/*************************************************************************** * Bezparametrická verze testu, která své výsledky nezobrazuje * v dialogovém okně, ale posílá je na standardní výstup. */ public static void ttest() { System.out.println("\n==============================================="); i = 1; tiskni( "start"); j = i++; tiskni( "j = i++" ); k = ++i; tiskni( "k = ++i"); k = i-- + j--; tiskni( "k = i-- + j--"); k = --i + --j; tiskni( "k = --i + --j"); } /*************************************************************************** * Pomocná metoda, která vytiskne zadanou akci na standarní výstup. */ private static void tiskni( String akce ) { //Dvojice znaků \t v následujícím příkazu označuje znak tabulátoru System.out.println( "Po (" + akce + "):\ti=" + i + ", j=" + j + ", k=" + k ); }

Výše definovanou dvojici metod najdete na konci těla dříve stáhnutétřídy XkrementyRUP.

Po spuštění metody ttest() otevře BlueJ další, doposud neotevřené okno, kam vypisuje vše, co pošlete na standardní výstup. Toto okno můžete kdykoliv zobra-zit zadáním příkazu Zobrazit → Terminál.

Page 198: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

198 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 198 z 433

Obrázek 4.6 Okno terminálu zobrazující standardní výstup metody ttest

☺ Budete-li chtít, aby se vám tisky z minulých spouštění svých programůnemíchaly s stisky z nových spouštění, máte dvě možnosti:

v okně terminálu zadáte příkaz Nastavení → Smazat,

na výstup pošlete znak pro odstránkování (form feed) – např. jako řetězec "\f".

4.7 Metoda toString() V pasáži Textové řetězce na straně 177 jsme hovořili o tom, že každou instanci je možno převést na řetězec a že k tomu slouží metoda toString(). Doplňme nyní naši třídu její vlastní metodou toString(), aby nemusela být odkázána na metody získané „od cizích“.

Nedefinujete-li vlastní verzi metody toSAtring(), použije se verze defi-novaná systémem. Ta vrátí řetězec, který začíná názvem třídy, jejíž in-stanci na textový řetězec převádíme, následovaný znakem @ (šnek, zavináč) a číslem zapsaným v šestnáctkové soustavě. Někdy takovýtopřevod vyhovuje, ale většinou budeme chtít definovat vlastní, který bypro nás byl více informativní.

Dohodněme se, že metoda toString() instancí třídy Strom se bude chovat stejně jako obdobné metody tříd grafických obrazců, tj. že vypíše jméno třídy, pořadí in-

Page 199: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 199

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 199 z 433

stance a za ním aktuální souřadnice a rozměr dané instance. Výsledný program by mohl vypadat např. následovně: 1 2 3 4 5 6 7 8 9

10 11

/*************************************************************************** * Převede instanci na řetězec obsahující název třídy, pořadí instance, * její souřadnice a rozměry. * * @return Řetězcová reprezentace dané instance. */ public String toString() { return "Strom_" + pořadí + ": x=" + getX() + ", y=" + getY() + ", šířka=" + getŠířka() + ", výška=" + getVýška(); }

Metodu hned vyzkoušíme. Vytvořte novou instanci třídy Strom pomocí bezpara-metrického konstruktoru a pak zavolejte metodu zpráva(text) třídy P a předejte jí tuto instanci jako parametr. Máte-li vše naprogramováno správně, metoda otevře dialogové okno z obr. 4.7.

Obrázek 4.7 Řetězec, na nějž převede instanci třídy Strom metoda toString, vytištěný v okně

Doplňte metodou toString i svoji vlastní třídu z minulé kapitoly.

Podoba třídy Strom, do které jsme ji doposud dovedli, je uložena ve tří-dě Strom_4, kterou najdete v projektu 03_Třídy_Z.

4.8 Prázdná standardní třída Ještě jednou se vrátím k vytváření nové třídy. Doposud jsme při jejím vytváření nastavovali přepínač do polohy Prázdná třída. Od této chvíle však budu využívat šablony, po níž BlueJ sáhne při nastavení přepínače druhu vytvářené třídy do po-lohy Standardní třída. V takovém případě BlueJ otevře soubor, v němž již bude série

Page 200: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

200 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 200 z 433

řádkových komentářů naznačujících doporučované pořadí vkládaných atributů a metod, atributy počet a pořadí generující „rodné číslo“ vytvářené instance a bu-dou zde zároveň připraveny prázdné definice bezparametrického konstruktoru a metody toString().

Připomínám, že uvedenou podobu dále uvedené standardní prázdné třídy mohou využívat pouze ti, kdo si instalovali modul rup. Standard-ní prázdná třída dodávaná s originální instalací vypadá trochu jinak.

Máte-li instalovaný modul rup a vytvoříte-li standardní třídu s názvem PrázdnáTřída, obdržíte následující program. Projděte si jej a připomeňte si přitom vše, co jsme doposud probrali. Nevzrušujte se prozatím komentáři, které hovoří o věcech, jež jsme ještě neprobrali – postupně si o všech povíme. 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 30 31 32 33 34 35 36 37

/******************************************************************************* * Třída PrázdnáTřída slouží k ... * * @author jméno autora * @version 0.00.000, 0.0.2003 */ public class PrázdnáTřída { //== KONSTANTNÍ ATRIBUTY TŘÍDY ================================================= //== PROMĚNNÉ ATRIBUTY TŘÍDY =================================================== /** Celkový počet vytvořených instancí. */ private static int počet = 0; //== KONSTANTNÍ ATRIBUTY INSTANCÍ ============================================== /** Rodné číslo instance = jako kolikátá byla vytvořena. */ private final int pořadí = ++počet; //== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================ //== PŘÍSTUPOVÉ METODY ATRIBUTŮ TŘÍDY ========================================== //== OSTATNÍ METODY TŘÍDY ====================================================== //############################################################################## //== KONSTRUKTORY A METODY VRACEJÍCÍ INSTANCE VLASTNÍ TŘÍDY ==================== /*************************************************************************** * Bezparametrický konstruktor ... */ public PrázdnáTřída() { } //== PŘÍSTUPOVÉ METODY ATRIBUTŮ INSTANCÍ =======================================

Page 201: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 201

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 201 z 433

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

//== PŘEKRYTÉ METODY IMPLEMENTOVANÝCH ROZHRANÍ ================================= //== PŘEKRYTÉ ABSTRAKTNÍ METODY RODIČOVSKÉ TŘÍDY =============================== //== PŘEKRYTÉ KONKRÉTNÍ METODY RODIČOVSKÉ TŘÍDY ================================ /*************************************************************************** * Vrací textovou reprezentaci dané instance * používanou především k ladícím účelům. * * @return Název třídy následovaný podtržítkem a "rodným číslem" instance. */ public String toString() { return getClass().getName() + "_" + pořadí; //return P.jménoTřídy( this ) + "_" + pořadí; } //== NOVĚ ZAVEDENÉ METODY INSTANCÍ ============================================= //== SOUKROMÉ A POMOCNÉ METODY TŘÍDY =========================================== //== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ======================================== //== VNOŘENÉ A VNITŘNÍ TŘÍDY =================================================== //== TESTY A METODA MAIN ======================================================= }//public class PrázdnáTřída

Na řádcích 1 – 6 je dokumentační komentář třídy. Zde vyplňte funkci a účel třídy a u složitějších tříd zde můžete dodat i nějaký pomocný výklad k jejich používání.

Komentáře na řádcích 9, 10 a 16 označují příslušné sekce atributů.

Atribut počet na řádku 13 slouží k uchování celkového počtu doposud vytvo-řených instancí. Je spřažen s atributem pořadí (řádek 19), v němž se uchovává číslo, označující kolikátá je daná instance mezi doposud vytvořenými instan-cemi. To už jsme si všechno ukazovali.

Komentáře na řádcích 22 – 24 označují další sekce zdrojového kódu. O všech z nich jsme si již vyprávěli.

Na řádku 27 vás možná zarazilo, že vedle konstruktorů jsou i jiné metody vracející odkaz a instance této třídy. Nicméně jste se s podobnou metodou již setkali – je jí metoda getPlátno ve třídě Plátno.

Komentáře na řádcích 37 až 40 hovořící o překrytých metodách zatím igno-rujte. Jejich význam si budeme postupně objasňovat v následujících kapito-lách.

Tělo definice metody toString() vás možná zarazí. Je upravené tak, aby pra-covalo vždy nezávisle na tom, máte-li v projektu definovanou třídu P s jejími

Page 202: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

202 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 202 z 433

statickými metodami. Definici využívající třídu P získáte tak, že první řádek (50) smažete a zrušíte komentářová lomítka na počátku druhého řádku (51).

Zbylé komentáře by vám měly být jasné. Jedinou výjimkou je komentář na řádku 58 hovořící o vnořených a vnitřních třídách. Na ty dojde až v samém závěru učebnice.

Jak jsem již řekl, všechny programy, které jsem pro vás a tuto učebnici připravil, jsou vytvořeny pomocí této šablony.

Pokud vám pořadí deklarací a definic nesedí, můžete si zvolit jiné. Určitě vám však doporučuji si nějaké pořadí zavést a zvyknout si na ně. I takové jedno-duché třídy, jakou je např. náš strom, mívají dlouhé zdrojové kódy s řadou atribu-tů a metod a zavedení obdobného pořadí může urychlit jejich vyhledávání.

4.9 V útrobách testovací třídy V podkapitole Testování na straně 104 jsme se naučili vytvářet testovací třídy ob-sahující testovací přípravky a testy, které si vždy nejprve nechali připravit přípra-vek a nad ním pak test provedly. Zároveň jsme si zde řekli, že v moderním programování se začíná stále více prosazovat metodika TDD, podle níž je třeba nejprve napsat testy a teprve pak vlastní program.

Vraťme se ale k testovacím třídám prostředí BlueJ. Testovací třída je obyčejná třída jako každá jiná. Pravda, používá některé technologie, které jsme si prozatím nevysvětlili, ale jinak na ní není nic zvláštního. Jediná zvláštní je, že ji za nás BlueJ připravil automaticky, takže jsme ji nemuseli programovat.

Často se vyskytují situace, kdy pomocí takovéto automaticky vytvořené třídy nedokážeme dost dobře definovat test, který by splňoval všechny naše požadav-ky, a je proto vhodné mu pomoci „ručně“. K tomu ale potřebujeme vědět, jak vlastně taková testovací třída pracuje.

Vytvoříme si nyní proto novou, prázdnou testovací třídu a vysvětlíme si na ní mechanizmy, které automatické testování umožňují, a ukážeme si také, jak BlueJ testy vytváří.

Testovací třídu můžeme vytvořit dvěma způsoby:

v místní nabídce třídy, kterou budete chtít testovat, zadáte příkaz Vytvořit testo-vací třídu,

požádáte o vytvoření nové třídy (např. stisknutím tlačítka Nová třída) a v ná-sledně otevřeném dialogovém okně nastavíte přepínač do polohy Test jednotky pro BlueJ.

Page 203: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 203

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 203 z 433

V obou případech se vytvoří testovací třída podle stejné šablony. Rozdíl bude pouze v tom, že při vytváření nové třídy musíte zadat název testovací třídy vy, kdežto třída vytvořená z místní nabídky jiné třídy odvodí svůj název od názvu té-to třídy (viz naše třída SmrkTest) a navíc se v diagramu tříd bude snažit této třídy „držet“. (Zkuste s testovanou třídou v diagramu zahýbat a uvidíte, jak ji bude tes-tující třída následovat.)

Třídám vytvořeným klasickým způsobem musíte vymyslet název sami a ne-závisle na názvu se na žádnou jinou třídu „nelepí“. Jinak s nimi ale můžete dále pracovat zcela stejně jako s třídami vytvořenými z místní nabídky.

„Přilepené“ testovací třídy získáte opravdu pouze zadáním příkazu v místní nabídce testované třídy. Získáte-li třídu jakkoliv jinak, už se na testovanou třídu lepit nebude, a to ani tehdy, převezmete-li obě třídy (tj. testovanou a testovací) z jiného projektu, kde se na sebe „lepí“.

Dost již ale doprovodných řečí a pojďme vytvořit plánovanou testovací třídu. Abychom viděli shodnosti a rozdíly, vytvoříme ji nezávisle na ostatních třídách: stiskněte tlačítko Nová třída a zadejte, že chcete vytvořit Test jednotky pro BlueJ nazvaný Test_04.

Otevřete nyní soubor s jejím zdrojovým kódem a prohlédněte si jej. Měl by vypadat následovně: 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

/******************************************************************************* * Testovací třída Test_04. * * @author jméno autora * @version 0.00.000, 0.0.2003 */ public class Test_04 extends junit.framework.TestCase { //############################################################################## //== PŘÍPRAVA A ÚKLID PŘÍPRAVKU ================================================ /*************************************************************************** * Vytvoření přípravku (fixture), tj. sady objektů, s nimiž budou všechny * testy pracovat a která se proto vytvoří před spuštěním každého testu. */ protected void setUp() { } /*************************************************************************** * Úklid po testu - tato metoda se spustí po vykonání každého testu. */ protected void tearDown() {

Page 204: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

204 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 204 z 433

26 27 28 29 30

} //== VLASTNÍ TESTY ============================================================= }//public class Test_04 extends junit.framework.TestCase

Třída obsahuje připravený konstruktor a dvě prázdné metody: setUp() a tearDown(). Jak si můžete přečíst v komentářích, metoda setUp() má na starosti vytvoření přípravku před spuštěním vlastního testu a metoda tearDown() pak má za úkol po skončení testu „uklidit“.

Přípravek Jak jistě odhadnete, kód potřebný pro definici přípravku vkládá BlueJ do metody setUp(), která je doposud prázdná. Vyzkoušejte si to. Chcete-li získat stejný zdro-jový kód jako já, postupujte následovně:

1. Restartujte virtuální stroj.

2. Pošlete třídě Plátno zprávu getPlátno() a získaný odkaz uložte do zásobníku odkazů.

3. Vytvořte nový obdélník pomocí bezparametrického konstruktoru.

4. Vytvořte novou elipsu na souřadnicích x=0, y=50 s rozměry šířka=50, výška=25.

5. Vytvořte nový trojúhelník na souřadnicích x=50, y=50 s rozměry šířka=50, výška=50.

6. Změňte rozměr obdélníku na šířka=50, výška=25.

7. Změňte pozici trojúhelníku na x=0, y=50.

8. Požádejte třídu Test_04, aby ze zásobníku odkazů vytvořila testovací přípra-vek.

Podíváte-li se nyní na zdrojový kód třídy, zjistíte dvě změny:

na počátku těla třídy se objevily deklarace atributů,

tělo metody setUp() bylo naplněno kódem definujícím vytvoření potřebných instancí a ukládajícím odkazy na tyto instance do připravených atributů.

1 2 3

public class Test_04 extends junit.framework.TestCase { private Plátno plátno1;

Page 205: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 205

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 205 z 433

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 30 31 32 33 34

private Obdélník obdélník1; private Elipsa elipsa1; private Trojúhelník trojúhel1; //############################################################################## //== PŘÍPRAVA A ÚKLID PŘÍPRAVKU ================================================ /*************************************************************************** * Přípravek (fixture) - inicializace spouštěná před každým testem. */ protected void setUp() { plátno1 = Plátno.getPlátno(); obdélník1 = new Obdélník(); elipsa1 = new Elipsa(50, 0, 50, 25); trojúhel1 = new Trojúhelník(50, 50, 50, 50); obdélník1.setRozměr(50, 25); trojúhel1.setPozice(50, 0); }//protected void setUp() /*************************************************************************** * Úklid spouštěný po každém testu. */ protected void tearDown() { }//protected void tearDown() //== VLASTNÍ TESTY ============================================================= }//public class Test_04 extends junit.framework.TestCase

V postupu, podle kterého jste připravovali obsah zásobníku odkazů, jsem schvál-ně postupoval „neoptimálně“ a některé parametry vytvářených instancí dodateč-ně upravoval. Chtěl jsem vám ukázat, že při vytváření přípravku BlueJ postupuje tak, že do metody setUp() zanese postupně všechny příkazy, které jste před tím zadávali.

Z předchozího výkladu je vám již asi jasné, proč musíme před vytvoře-ním sady odkazů určených k uložení do přípravku restartovat virtuální stroj. Požádáte-li BlueJ o vytvoření přípravku až po delším experimen-tování, zanese do metody setUp() záznam o veškerém tomto experi-mentování. Je proto výhodné po předchozích experimentech před přípravou požadované podoby zásobníku odkazů nejprve restartovat virtuální stroj. BlueJ pak zanese pouze akce provedené po tomto restar-tu.

Je zřejmé, že současná podoba metody setUp() je zbytečně složitá. Nic nám proto nebrání ji zjednodušit. Smažte v těle metody poslední dva příkazy a upravte

Page 206: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

206 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 206 z 433

předchozí volání konstruktorů tak, abychom požadovanou podobu přípravku získali hned po konstrukci jednotlivých objektů. Upravená metoda by měla vypa-dat následovně: 1 2 3 4 5 6 7

protected void setUp() { plátno1 = Plátno.getPlátno(); obdélník1 = new Obdélník(0, 0, 50, 25); elipsa1 = new Elipsa(50, 0, 50, 25); trojúhel1 = new Trojúhelník(50, 0, 50, 50); }

Vytvořený přípravek můžete upravovat nejenom ručně, ale i s využitím podpory BlueJ tak, jak jsme si to ukazovali v kapitole Testování. Budete-li chtít přípravek později doplnit, můžete to udělat tak, že (po restartu VM) provedete všechny po-žadované akce včetně případného naplnění zásobníku přípravkem a dodání dal-ších odkazů a výslednou podobu necháte uložit jako přípravek (to už jsme si ukazovali v kapitole Úprava obsahu přípravku na straně 107).

Když si po této změně prohlédnete obsah metody setUp(), najdete v ní nový seznam příkazů, přičemž na místě, kde jste naplňovali zásobník odkazů minulým přípravkem, budou opsány příkazy z těla minulé verze metody setUp(). Vyzkou-šejte si to.

Automaticky generované testy Podívejme se nyní, co se stane, když vytvoříme nějaký automaticky generovaný test. Požádejte třídu Test_04 o vytvoření nového testu nazvaného ZměnaVelikostiPlátna, v němž zmenšíte plátno na rozměr šířka=100, výška=200 a necháte znovu zobrazit všechny instance.

Když ukončíte záznam testu a podíváte se do zdrojového kódu třídy Test_04, zjistíte, že se na jeho konci objevila nová metoda: 1 2 3 4 5 6 7 8

public void testZměnaVelikostiPlátna() { plátno1.setRozměr(100, 200); obdélník1.nakresli(); elipsa1.nakresli(); trojúhel1.nakresli(); strom1.nakresli(); }

Page 207: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 207

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 207 z 433

Stejně jako u přípravku platí, že tento automaticky vygenerovaný kód můžete jakkoliv upravit. BlueJ není nijak ješitný a budete-li jeho výtvory upravovat, ne-bude se bouřit.

Vlastní testy Rozhodnete-li se jednou upravovat zdrojový kód testovací třídy, nemusíte se omezovat pouze na úpravu automaticky generovaných testů. Nic vám nebrání vytvářet testy vlastní. Má-li však BlueJ chápat vámi napsaný test opravdu jako text, musíte při psaní testovací metody dodržet několik pravidel:

její jméno musí začínat předponou test a pokračovat názvem testu,

musí být veřejná,

nesmí mít žádné parametry.

Při tvorbě testu musíte zároveň počítat s tím, že se před spuštěním vlastního testu nejprve spustí metoda setUp().

Definujme jednoduchý test, který nazveme testProhození a který převrátí umístění jednotlivých obrazců na plátně – pravé prohodí s levými a horní s dol-ními. Vše potřebné již znáte, takže si můžete zkusit naprogramovat test nejprve sami.

Tak co, funguje? Pokud ne, zkuste jej nejprve rozchodit. Pokud vám již fun-guje (a nebo jenom nemáte trpělivost hledat chyby), zkuste svůj návrh porovnat s následujícím řešením (pokud se liší, nevadí – hlavně, že funguje): 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19

public void testProhození() { //Aby bylo prohození vidět co nejlépe, vypíšeme nejprve upozornění P.zpráva("Bude se prohazovat"); //Nejprve je třeba některé obrazce přesunout jinam, //aby se při přesunech vzájemně neumazávali. //To, že se budou v odkladné pozici překrývat, nevadí. obdélník1.setPozice(200,0); elipsa1.setPozice(200,0); trojúhel1.setPozice(200, 0); //Strom je poslední - proto jej již nikdo nepřemaže //Nyní přesuneme obrazce do cílových pozic strom1.setPozice(50,0); obdélník1.setPozice(50,150); elipsa1.setPozice(0, 50); trojúhel1.setPozice(0, 0); }

Page 208: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

208 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 208 z 433

Úklid Zásobník odkazů a některé další objekty je třeba nejenom před testem připravit, ale také po testu uklidit.

Spustíte-li náš test dvakrát po sobě, budou při upozornění na chystané pro-hození na plátně dvě sady obrazců, což určitě není to, co bychom chtěli. Do ob-dobně nepříjemného stavu se dostanete i tehdy, když spustíte nejprve test změny velikosti plátna a po něm test prohození.

Je to proto, že po sobě naše testy neuklízely. Hned to napravíme. K úklidu po provedeném testu slouží metoda tearDown(), kterou systém spustí automaticky po každém testu.

V našem případě, kdy účelem testů je ukázat na plátně, že se obrazce chovají tak, jak mají, ale nemůžeme uvést plátno do počátečního stavu hned po provedení testu, protože by uživatel neměl možnost si výsledný obrázek prohlédnout a ově-řit, že výsledek odpovídá očekávání. Musíme proto před vlastní úklid ještě předřadit dotaz, který uživateli umožní zkontrolovat výsledek. Výsledný uklízecí program by pak mohl vypadat např. následovně: 1 2 3 4 5

protected void tearDown() { assertTrue( P.souhlas("V pořádku?") ); plátno1.setRozměr(300, 300); }

Metody assertEquals a assertTrue V předchozím programu jsme výsledek dotazu na to, zda je vše v pořádku, pře-da-li jako parametr metodě assertTrue(boolean). Tato metoda je jednou ze série potvrzovacích metod, kterými v průběhů našich testů ověřujeme, že je vše v po-řádku.

Základními potvrzovacími metodami jsou metody assertEquals(x1,x2), kte-rých je celá řada a liší se pouze tím, jakého typu jsou jejich parametry. Těmto me-todám předáme v prvním parametru očekávanou hodnotu a ve druhém hodnotu, ke které dospěl program. Pokud se tyto hodnoty liší, metody vypíší v testovacím okně očekávanou a obdrženou hodnotu a daný test ukončí.

V programu použitá metoda assertTrue(boolean) je vlastně jenom zestruč-něnou verzí metody assertEquals(boolean, boolean), které předáváme jako první parametr (tj. jako očekávanou hodnotu) logickou hodnotu true. Místo assertEquals( true, obdrženýVýsledek );

tak můžeme zapsat pouze

Page 209: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 209

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 209 z 433

assertTrue( obdrženýVýsledek );

Moc psaní nám to neušetří, ale leckomu (např. mně) připadá takový zápis pře-hlednější.

Dvouparametrické verze metod mají ještě tříparametrické kolegyně, které očekávají jako první parametr textový řetězec, za nímž následují testované hodno-ty. Tyto metody v případě neshody testovaných hodnot vypíší do testovacího ok-na nejprve zadaný řetězec, a teprve za ním zprávu o nesouhlasu testovaných hodnot.

Test testů Abyste lépe pochopili význam testovacích metod, připravil jsem pro vás třídu TestTestů (najdete ji v projektu 06_Rozhraní_Z), která vám použití potvrzovacích me-tod předvede. Podívejme se, co vše nám může ukázat. 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 30 31 32 33 34 35

/******************************************************************************* * Testovací třída TestTestů sloužící k demonstraci základních vlastností testů. * * @author Rudolf Pecinovský * @version 2.01, duben 2004 */ public class TestTestů extends junit.framework.TestCase { private int i1; private String s1; private Elipsa e; //############################################################################## //== PŘÍPRAVA A ÚKLID PŘÍPRAVKU ================================================ /*************************************************************************** * Vytvoření přípravku (fixture), tj. sady objektů, s nimiž budou všechny * testy pracovat, a která se proto vytvoří před spuštěním každého testu. */ protected void setUp() { System.out.println( "\n=== Příprava testu " + getName() ); i1 = 1; s1 = "Jedna"; e = new Elipsa(); } /*************************************************************************** * Úklid po testu - tato metoda se spustí po vykonání každého testu. */ protected void tearDown() { Plátno.smaž(); System.out.println( "XXX Uklizeno po testu " + getName() );

Page 210: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

210 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 210 z 433

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

} //== VLASTNÍ TESTY ============================================================= public void testČísel() { System.out.println( "Čísla souhlasí" ); assertEquals( "Neshoda celého čísla", 1, i1 ); System.out.println( "Čísla nesouhlasí" ); assertEquals( "Neshoda celého čísla", 0, i1 ); System.out.println( "Konec testu čísel" ); } public void testSouřadnic() { System.out.println( "Souřadnice souhlasí" ); assertEquals( "Neshoda souřadnic", 0, e.getX() ); System.out.println( "Souřadnice nesouhlasí" ); assertEquals( "Objekty se liší", null, e ); assertEquals( "Neshoda souřadnic", 1, e.getX() ); System.out.println( "Konec testu souřadnic" ); } public void testŘetězců() { System.out.println( "Řetězce souhlasí" ); assertEquals( "Neshoda textů", "Jedna", s1 ); System.out.println( "Řetězce souhlasí" ); assertEquals( "Neshoda textů", "Dva", s1 ); System.out.println( "Konec testu řetězců" ); } }//public class TestTestů extends junit.framework.TestCase

Na počátku (řádky 9-11) jsem deklaroval tři atributy, jimž metoda setUp() přiřadí hodnoty, které pak budeme v testovacích metodách testovat.

Metoda setUp() na řádcích 20 až 26 nejenom vytvoří přípravek (tj. inicializuje potřebné proměnné), ale před tím nejprve vypíše na standardní výstup, že test je připraven. Využije přitom toho, že může zavoláním metody getname() zjistit, jak se test jmenuje.

Obdobně metoda tearDown() na řádcích 32 až 36 nejenom po testu uklidí, ale současně také na závěr vypíše, že je již uklizeno.

Na řádcích 41 až 67 jsem pro vás připravil tři testy. Každý z nich nejprve vy-píše na standardní výstup, že začal, pak dvakrát vypíše zprávu o chystané akci následovanou voláním potvrzovací metody. Poprvé na dvě hodnoty, které jsou shodné, podruhé na hodnoty, které se liší.

Prozatím jsou všechny tři testy nastaveny tak, aby v polovině zkolabovaly na rozdílnosti hodnot. Spustíte-li testy po resetovaném virtuálním stroji, získáte vý-pis, který si můžete prohlédnout na obr. 4.8.

Page 211: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 211

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 211 z 433

Obrázek 4.8 Kontrolní tisky posílané testem testů na standardní výstup

Chtěl jsem vám na tomto příkladu znovu připomenout, že neprojití jednoho testu neovlivní chod testů dalších. Změnou očekávaných hodnot můžete dosáhnout to-ho, že celý test projde nebo naopak zkolabuje hned na první potvrzovací metodě.

4.10 Debugger a práce s ním Známá programátorská zásada říká, že v každém programu je nejméně jedna chyba. Případy, kdy je programátorovi při výskytu chyby ihned jasné, co je třeba opravit, však nebývají příliš časté. Mnohem častější je případ, kdy mu vůbec není jasné, co danou chybu způsobuje, a musí proto chvíli hledat, než se mu podaří chybu najít a opravit.

Při tomto hledání programátoři nejčastěji používají dvě techniky:

vkládají do programu příkazy, které jim někam vypíší aktuální stav progra-mu (nejčastěji hodnoty atributů a lokálních proměnných),

používají pomocný program zvaný debugger, který jim umožní program krokovat (provádět jej příkaz za příkazem) a přitom sledovat, co vlastně dělá.

My se v této podkapitole zaměříme právě na onu druhou techniku a ukážeme si, co nám v tomto směru BlueJ nabízí.

Page 212: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

212 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 212 z 433

Krokování programu Otevřete si zdrojový kód třídy Test_04 a zobrazte tělo metody setUp(). Klepněte ukazatelem myši do levého sloupce, kde se zobrazují čísla řádků (postup je stejný i v případě, kdy čísla řádků nezobrazujete) do řádku, ve kterém se konstruuje elipsa (na obrázku 4.9 řádek 81).

Obrázek 4.9 Nastavení zarážky na řádku 81

BlueJ vloží do daného místa programu zarážku (anglicky breakpoint) a v editoru v daném místě zobrazí malou ikonu symbolizující dopravní značku Stůj, dej před-nost v jízdě. Až bude program procházet tímto místem, virtuální stroj zastaví jeho provádění a BlueJ nám zobrazí místo v programu, na němž se nacházíme, spolu s informacemi oé aktuálních hodnotách atributů a lokálních proměnných.

Vše si hned vyzkoušíme. Vraťte se do okna projektu a spusťte test prohození. Program se rozběhne, zobrazí plátno, na něm obdélník a pak se zastaví a zobrazí svůj aktuální stav. Vzhled části obrazovky mého počítače se všemi čtyřmi zobra-zenými okny si můžete prohlédnout na obr. 4.10.

Page 213: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 213

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 213 z 433

Obrázek 4.10 Vzhled části obrazovky po zastavení programu na zarážce

Okna na obrázku jsem uspořádal tak, aby se vešla na obrazovku o rozměrech 800×600 bodů. Používáte-li vyšší rozlišení (a při programování vám to vřele dopo-ručuji), můžete zařídit, aby se vám jednotlivá okna překrývala méně nebo dokon-ce vůbec.

Vraťme se ale k obrázku a povězme si, co je na něm k vidění nového. Vyku-kující roh plátna i část okna projektu nám zatím nic nového neřeknou. Soustředí-me se proto na zbylá dvě okna.

V okně editoru vidíte zobrazenou část kódu, kde se program při svém pro-vádění zarazil. Abyste to místo dobře poznali, tak BlueJ příslušný řádek podšedí a ve sloupečku s čísly řádku překryje ikonu zarážky černou šipkou.

Kromě toho se otevřelo okno s titulkem BlueJ: Debugger, se kterým jsme se do-posud nesetkali. Na toto okno zaměříme svoji další pozornost. Při jeho spodním okraji najdete pět velkých tlačítek:

Page 214: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

214 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 214 z 433

Tlačítko Zastavit slouží k zastavení běžícího programu. Pokud se vámdiagram např. zacyklí, tak jej můžete tímto tlačítkem zastavit a po-dívat se, kde se toulá.

Stiskem tlačítka Krokovat provedete daný příkaz. Je-li na řádku se za-rážkou více příkazů, provedou se všechny z nich. Je-li součástí pří-kazu volání metod, metody se provedou.

Tlačítko Krokovat dovnitř má skoro stejnou funkci jako tlačítko Kroko-vat. Rozdíl se projeví až ve chvíli, když je součástí příkazu volánímetod. Po stisku tohoto tlačítka se totiž metoda neprovede celá.Pouze se zavolá a provádění se zarazí na prvním příkazu těla vola-né metody.

Tlačítko Pokračovat použijete ve chvíli, kdy již víte vše, co jste se chtě-li od dané části kódu dozvědět. Po jeho stisku se zruší krokování aprogram pokračuje ve svém normálním běhu až do konce či dopříští zarážky.

Stiskem tlačítka Ukončit ukončíte provádění programu. Použijete jenapř. ve chvíli, když jste již zjistili, kde je chyba a chcete ji opravit.(Horší variantou je, že si uvědomíte, že jste přeběhli kritické místo amusíte program spustit znovu, abyste příště zastavili před ním.)

Takže víme, co potřebujeme a můžeme se pustit do krokování. Stiskněte tlačítko Krokovat a ověřte, že se příslušný příkaz ihned provedl a na plátno se nakreslila elipsa. Znak zarážky zůstal tam, kde byl, ale šipka se spolu s podšedění přesunula na další řádek.

Stiskněte znovu tlačítko Krokovat a přidejte na plátno trojúhelník, který se na-kreslí přes elipsu. Šipka označující aktuální řádek se přesune na řádek s příkazem pro vytvoření instance stromu.

Na tento příkaz si posvítíme trochu podrobněji a podíváme se, jak se vlastně provádí. Stiskněte proto tlačítko Krokovat dovnitř.

Program se v tuto chvíli dozvěděl, že bude potřebovat pracovat s třídou Strom – musí si proto třídu nejprve připravit. A protože jsme stiskem tlačítka Kro-kovat dovnitř projevili značnou zvědavost, budeme moci přípravu třídy sledovat.

Třída Strom má tři atributy třídy. Prvé dva jsou konstanty, jejichž hodnotu do-kázal překladač zjistit již při překladu, a proto s tím při přípravě třídy již „neotra-vuje“. Třetí atribut je ale běžným atributem a je mu proto potřeba přiřadit počáteční hodnotu. Debugger proto otevře okno se zdrojovým kódem třídy Strom a nastaví šipku na příkaz, který přiřazuje atributu krok počáteční hodnotu 50.

Podíváte-li se do okna debuggeru, zjistíte, že v části určené pro atributy třídy jsou již všechny tři atributy uvedeny, ale atribut krok má prozatím přiřazenu po-

Page 215: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 215

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 215 z 433

čáteční hodnotu 0. Správnou počáteční hodnotu získá až po provedení přiřazení uvedeného v deklaraci.

Stiskněte tlačítko Krokovat. Tím se atributu krok přiřadí požadovaná hodnota. Protože je tím příprava třídy Strom ukončena, debugger vrátí řízení metodě, která chtěla udělat něco (konkrétně vytvořit instanci), kvůli čemuž musela být připra-vena třída. Šipka ukazující příkaz, který se má vykonat, se proto nepohnula. Jedi-nou změnou od stavu, kdy jsme se na ni dívali minule, je, že nyní je již třída připravena k použití a můžeme ji poslat zprávu – požádat ji o vytvoření instance.

Znovu tedy stiskneme tlačítko Krokovat dovnitř. To nás znovu přesune do zdro-jového kódu třídy Strom, avšak tentokrát již do těla konstruktoru. V těle je jediný příkaz, který volá čtyřparametrickou verzi konstruktoru. Stiskem Krokovat dovnitř se přesuneme do jeho těla a tam akci ještě jednou zopakujeme. Tím se konečně do-staneme do těla konstruktoru, který již nikoho jiného za sebe pracovat neposílá a udělá vše potřebné sám (viz obr. 4.11).

Obrázek 4.11 Po několika vnořeních jsme se přesunuli do těla konstruktoru,

který provede všechny potřebné akce spojené s konstrukcí instance

Okno debuggeru Konečně jsme se dostali do metody, ve které si budeme moci ukázat nejrůznější možnosti, které nám nabízí okno debuggeru. Opusťme proto na chvíli krokování a podívejme se blíže na okno debuggeru (viz obr. 4.12).

Page 216: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

216 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 216 z 433

Obrázek 4.12 Okno debuggeru

Pracovní plocha okna je rozdělena na pět oblastí, jejichž vzájemnou velikost mů-žete měnit uchopením a přesunutím jejich hranic pomocí myši, jak je ukázáno na obrázku.

Vlákna Horní široká oblast je vyhrazena pro informace o spuštěných vláknech. Vlákny jsou přitom myšleny samostatné procesy, které vykonávají relativně nezávislé úlohy – např. jedno vlákno může kreslit obrázek zatímco druhé otevře dialogové okno a čeká na vaše vyjádření k tomu, co páchá vlákno první.

Pořadí volání Levá oblast popisuje situaci na tzv. zásobníku návratových adres. Zde můžeme zjistit, kdo koho volal a v jakém místě, přičemž naposledy volaná metoda je zob-razena nahoře.

Z výpisu na obr. 4.12 můžeme poznat, že jsme v konstruktoru (symbol <init> označuje konstruktor) třídy Start, který byl volán jiným konstruktorem, ten zase

Page 217: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 217

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 217 z 433

jiným a ten byl volán metodou setUp třídy Test_04. Další metody jsou již systé-mové (mají na starosti správné spuštění testu), a proto je zde nebudu dále rozebí-rat.

Klepnutím na kteroukoliv z položek tohoto seznamu otevřete editor se zdro-jovým kódem třídy, v níž se metoda nachází (musí to být třída z vašeho projektu). Editor zobrazí právě vykonávaný řádek. Ten bude pro vyšší názornost podšeděný a označený šipkou. Tak můžete relativně zjistit, kudy se program dostal k místu, kde jste jej zastavili.

Klepnete-li náhodou na položku, jejíž zdrojový kód nemá BlueJ k dispozici, otevře dialogové okno, v němž vás na tuto skutečnost upozorní.

Obrázek 4.13 Reakce prostředí na skutečnost, že chcete krokovat třídu, od níž nemáte zdrojový kód.

Atributy třídy Vpravo nahoře je zóna vyhrazená pro atributy třídy. Jedná se přitom o atributy té třídy, ve které se nachází aktivní položka z oblasti Pořadí volání. Ověřte si, že když klepnete na položku Test_04.setUp, objeví se zde místo atributů třídy Strom atributy třídy Test_04.

Atributy instancí Pod oblastí pro atributy třídy je oblast vyhrazená pro atributy instancí. Zase zde najdete pouze atributy té instance, jejíž metodu máte zrovna označenou v Pořadí vo-lání.

Lokální proměnné Jak nadpis napovídá, v třetím panelu najdete lokální proměnné metody. Stejně ja-ko v předchozích dvou polích se i zde jedná o metodu zvýrazněnou v panelu Pořa-dí volání.

Zde bych jenom připomenul, že mezi lokální proměnné se počítají i paramet-ry. Ostatně v tuto chvíli v poli ani jiné lokální proměnné neuvidíte, protože pro-gram si své lokální proměnné vytváří až v okamžiku, kdy je opravdu potřebuje a

Page 218: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

218 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 218 z 433

teprve tehdy se o nich dozví i debugger, který se vám snaží zobrazit jejich hodno-ty.

Můžete si to hned vyzkoušet. Když nyní stisknete tlačítko Krokovat, vytvoří se (a zobrazí v debuggeru) proměnná výškyKmene a přiřadí se jí spočtení hodnota – v našem případě 50. Při dalších krocích se postupně vytvoří proměnné výškaKoruny, šířkaKmene a posunKmene.

Při krokování některých metod můžete být zaskočení tím, že vám de-bugger nezobrazí všechny lokální proměnné. Nejčastější příčinou toho-to nedorozumění je to, že debugger zobrazuje pouze definované proměnné, tj. proměnné, které již mají vyhrazenu paměť a přiřazenoupočáteční hodnotu.

Pokud tedy lokální proměnnou pouze deklarujete, tj. pouze uvedete,jak se bude proměnná jmenovat a jakého bude typu, debugger ji igno-ruje. Všimne si jí až poté, co jí přiřadíte nějakou hodnotu.

Atributy a proměnné objektových typů Nyní nás ve zdrojovém textu konstruktoru stromu čeká vytvoření elipsy a přiřa-zení odkazu na ni do proměnné koruna. Podíváte-li se mezi atributy instance, zjis-títe, že jak koruna tak kmen mají indikovánu hodnotu null, která říká, že v danou chvíli neukazují nikam (vysvětlovali jsme si to v oddíle null na straně 148).

Popojeďme nyní dále a nechme program učinit další krok. Na plátně se objeví zelený kruh představující korunu budoucího stromu a debugger nám ve svém okně prozradí, že atribut koruna obsahuje odkaz na objekt (viz obr. 4.14).

Page 219: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 219

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 219 z 433

Obrázek 4.14 U objektových instancí a proměnných debugger pouze oznámí, že někam ukazují

Víc se z okna debuggeru nedozvíme. Musíme se smířit s tím, že nyní víme, že atribut koruna na rozdíl od atributu kmen někam ukazuje. Naštěstí u toho nemusí zůstat. Když pěkně poprosíme, je nám debugger ochoten prozradit víc. Poproste poklepáním na příslušný řádek a debugger otevře okno prohlížeče instancí, který vám prozradí hodnoty všech atributů primitivních typů (viz obr. 4.15).

Page 220: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

220 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 220 z 433

Obrázek 4.15 Poklepáním otevřeme okno prohlížeče pro danou instanci

Pravda, atribut barva je opět objektového typu, takže se znovu dozvíme pouze to, že někam ukazuje, ale poklepáním na něj otevřeme další okno prohlížeče, v němž se můžeme podívat barvě na zoubek.

Už nezastavuj – ruším zarážky Takto bychom mohli pokračovat až do konce. Krokování větších aplikací by ale bylo příliš dlouhé. Naštěstí tu máme ale tlačítko Pokračovat, které znovu rozběhne program bez krokování a jak jsme si již řekli, zastaví se až na další zarážce a ne-ní-li už v programu další zarážka, program v klidu skončí.

Problémem by mohlo být, kdyby byl program napsán tak, že by se na dané zarážce měl zastavit ještě stokrát. Pomoc je ale jednoduchá: klepnutím na ikonu zarážky tuto zarážku zrušíme a program může v klidu doběhnout.

Zarážky můžeme nejenom rušit, ale také zřizovat nové. Zjistíte-li, že o kousek dál je místo, na němž byste rádi zastavili, ale nechce se vám k němu „prokroková-vat“, můžete na příslušný řádek umístit zarážku a pak říci programu, aby pokra-čoval.

Předčasný konec programu Někdy se stane, že při krokování programu objevíte chybu a víte, že už nemá smysl pokračovat dále, protože musíte tuto chybu nejprve opravit. Snadná po-moc: stiskněte tlačítko Ukončit a program předčasně ukončete.

Vedle tlačítka v okně debuggeru nabízí BlueJ ještě jeden způsob předčasného zastavení programu: je jím známá možnost restartu virtuálního stroje. Když pro-

Page 221: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 221

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 221 z 433

gram běží, tak indikátor běhu programu změní své vybarvení ze šedobílého na červenobílé. Klepnete-li na něj pravým tlačítkem a zadáte-li Restartovat VM, program se předčasně ukončí.

Pozastavení běžícího programu Jsou chvíle, kdy se vám zdá, že program pracuje nějak podezřele dlouho, aniž by byly zřejmé jakékoliv výsledky jeho práce. Důvody mohou být dva:

Program někde otevřel dialogové okno a čeká, až mu odpovíte. Zejména při-práci na počátečních projektech bude asi tento případ mnohem častější. Po-kud okno nevidíte, musíte minimalizovat nebo alespoň odsunout všechna ostatní okna, abyste se probojovali k oknu, které na vás čeká a mohli stisk-nout požadované tlačítko.

Program opravdu někde něco dělá a vy byste chtěli vědět, jestli jste jej tak pomalý naprogramovali anebo jestli někde zabloudil.

V obou případech budou v takovéto situaci v okně debuggeru zhasnutá všechna tři střední tlačítka. Určitě bude aktivní tlačítko Ukončit, kterým můžete celý pro-gram předčasně ukončit. Mohlo by být aktivní také tlačítko Zastavit, ale museli bys-te se před tím alespoň jednou zastavit na nějaké zarážce. Dokud se nezastavíte na zarážce, debugger se tváří, že o tlačítku Zastavit neví.

Dáte-li si ale na nějaké dostatečně počáteční místo programu zarážku, pak tlačítko Zastavit ožije pokaždé, když program běží. Potřebujete-li se proto někdy uprostřed běhu programu dozvědět, kde se zrovna program nachází, stisknete je, okno debuggeru se zaplní stejně, jako při zastávce na zarážce a vy můžete začít klepat na jednotlivé položky v Pořadí volání a zjišťovat aktuální stav jednotlivých atributů a lokálních proměnných.

Když zjistíte, co jste potřebovali zjistit, stisknete tlačítko Pokračovat a program se zase rozběhne dál.

Krokování konstruktorů Při krokování konstruktorů musíte mít na paměti, že do konstrukce objektu patří i inicializace atributů definovaná v jejich deklaracích, která se spouští před spuště-ním vlastního těla konstruktoru. Chcete-li proto krokovat tuto inicializaci, musíte vložit zarážku na příslušný inicializační příkaz.

Page 222: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

222 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 222 z 433

4.11 Hodnotové a referenční typy V kapitole Konstanty a literály na straně 143 jsem hovořil o tom, že konstanty pri-mitivních typů bývají často deklarovány jako veřejné. U konstant objektových ty-pů je to složitější, protože tyto konstanty nemusejí být tak úplně konstantní, jako je tomu u typů primitivních. Rozdíl spočívá v tom, že v konstantách primitivních typů je uložena přímo hodnota, kdežto v konstantách objektových typů je pouze odkaz na instanci.

Když celočíselné konstantě přiřadíme počáteční hodnotu, víme, že tuto hod-notu bude mít po věky věků (přesněji dokud program nevypneme). U konstant objektových typů se můžeme spolehnout pouze na to, že se nikdy nezmění ulože-ný odkaz, tj. že konstanta bude odkazovat stále na tu samou instanci.

To, že odkaz na instanci je uložen v konstantě však ještě nic neříká o tom, co se bude dít s vlastní instancí. To záleží na tom, jak je daná třída definována. V této souvislosti se rozlišují tzv. hodnotové a referenční objektové typy. Podívejme se na vlast-nosti každé skupiny zvlášť.

Hodnotové typy Jak název napovídá, hodnotové typy slouží k tomu, aby uchovávali nějakou hod-notu. Máme-li dvě instance hodnotových typů, můžeme zjišťovat, obsahují-li stej-nou hodnotu obdobně, jako se na to můžeme ptát u dvou proměnných primitivních datových typů.

Z tříd, s nimiž jsme se doposud setkali, jsou hodnotovými typy String, Barva, Směr, Pozice a Rozsah. U jejich instancí má smysl se pídit po shodnosti hodnot, tj. ob-sahují-li dvě instance třídy String stejný řetězec či označují-li dvě instance třídy Po-zice stejné místo na plátně.

Chování instancí hodnotových tříd a z toho plynoucí jejich „konstantová spo-lehlivost“ se ale liší. Hodnotové třídy můžeme rozdělit do dvou skupin:

Neměnné (anglicky immutable) třídy nám nenabízejí žádnou možnost, jak změnit hodnotu uchovávanou v dané instanci. Konstanty neměnných hodno-tových typů se proto chovají naprosto stejně, jako hodnoty primitivních da-tových typů. Jakmile do nich uložíme odkaz na nějakou hodnotu, můžeme se spolehnout na to, že už budou vždycky odkazovat na tuto hodnotu. Konstan-ty těchto typů můžeme proto s klidným svědomím deklarovat jako veřejné. Z dosud používaných tříd sem patří třídy String, Barva a Směr.

Proměnné (anglicky mutable) třídy nám neměnnost uchovávaných hodnot nezaručí. Hodnota uchovávaná v jejich instanci se naopak může kdykoliv

Page 223: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 223

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 223 z 433

změnit. Odkazy na instance takovýchto tříd do konstant nepatří a už vůbec ne do veřejných konstant. Do této skupiny bychom zařadili třídy Pozice, Roz-sah.

Referenční datové typy U referenčních datových typů se po žádné hodnotě nepídíme. Mohli bychom říci, že jejich hodnotou je instance sama. Z tříd, s nimiž jsme doposud pracovali, by-chom do této skupiny mohli zařadit všechny grafické objekty. Např. to, že dva obdélníky mají stejnou barvu, jsou stejně veliké a na stejných souřadnicích nám nepřipadá jako důvod k tomu, abychom je považovali za shodné. Jsou to pro nás stále dva různé obdélníky, které se pouze v daný okamžik překrývají.

V řadě případů nás u instancí referenčních datových typů zajímá pouze to, o kterou instanci se jedná, a nijak nám nevadí, že její vlastnosti v průběhu času mění někdo cizí (např. že tvar posouvá nebo mění jeho rozměr). V takovýchto přípa-dech bývá výhodné deklarovat daný atribut jako veřejnou konstantu. Ta nám za-ručí, že nám nikdo nenahradí daný objekt jiným, avšak jinak přístup ostatních tříd k tomuto objektu nijak neomezuje.

Program demonstrující rozdíl Abychom nezůstali jenom u teorie, ukážu vám jednoduchý prográmek demon-strující rozdíly mezi hodnotovými a referenčními datovými typy. 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21 22

public static void testík() { //Definujeme tři proměnné hodnotového typu String String hodnota_1 = "První"; String hodnota_2 = "Druhý"; //Druhá a třetí proměnná bude odkazovat na stejný objekt String hodnota_3 = hodnota_2; //Definujeme tři proměnné referenčního typu Strom Strom odkaz_1 = new Strom(); Strom odkaz_2 = new Strom(); //Druhá a třetí proměnná bude odkazovat na stejný objekt Strom odkaz_3 = odkaz_2; System.out.println ( "Stav před úpravou:\n" + hodnota_1 + "\n" + hodnota_2 + "\n" + hodnota_3 + "\n\n" + odkaz_1 + "\n" + odkaz_2 + "\n" + odkaz_3 ); //Změním hodnotu objektu odkazovaného druhou proměnnou //U hodnotových typů bude objekt nahrazen jiným hodnota_2 += "+doplněk";

Page 224: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

224 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 224 z 433

23 24 25 26 27 28 29 30

//U odkazovaných objektů se změní pouze stav objektu odkaz_2.posunVpravo(); System.out.println ( "\n\nStav po úpravě:\n" + hodnota_1 + "\n" + hodnota_2 + "\n" + hodnota_3 + "\n\n" + odkaz_1 + "\n" + odkaz_2 + "\n" + odkaz_3 ); }

Metodu testik() najdete ve třídě HodnotyAOdkazy, kterou si můžete zkopírovat z projektu 03_Třídy_Z. Po jejím spuštění se v okně terminálu vypíše text, jenž si můžete prohlédnout na obr. 4.16.

Obrázek 4.16 Výsledek práce metody testík() třídy HodnotyAOdkazy

Před úpravou odkazuje proměnná hodnota_2 na stejný objekt jako proměnná hodnota_3 – tímto objektem je řetězec s textem "Druhý". Obdobně proměnná odkaz_2 odkazuje na stejný objekt jako proměnná odkaz_3 – obě ukazují na instanci stromu s názvem Strom_2.

Po provedených operacích na řádcích 22 a 25 se však tento stav změní. Obě „stromové“ proměnné budou stále ukazovat na stejný objekt. Tím, že jsme upravi-li objekt, na který ukazuje jedna z nich, jsme logicky upravili i objekt, na nějž uka-zuje ta druhá (aby ne, když ukazují na tentýž).

Page 225: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 225

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 225 z 433

U řetězcových proměnných, tj. proměnných typu String, je to jiné. Jakýkoliv pokus o úpravu objektu neměnného hodnotového datového typu vede k tomu, že se dané proměnné přiřadí odkaz na jiný objekt. Po úpravě proto ukazuje každá proměnná na jiný objekt. Proměnná hodnota_3 ukazuje pořád na původní řetězec, v němž je uložen text "Druhý".

Protože opomenutí popsaného chování řetězců patří k velmi častým za-čátečnickým chybám, tak vám to zopakuji ještě jednou: Datový typ String je neměnný hodnotový datový typ a jakýkoliv pokus o úpravu hodnoty jeho instance vede k vytvoření nové instance.

4.12 Projekt Zlomky Tak už jsme si toho napovídali pěknou řádku a je nejvyšší čas ukázat si, že pro-gramování není jenom o malování obrázků na plátno. Mám pro vás proto samo-statnou úlohu (budete si ale muset vzpomenout na hodiny matematiky na základní škole):

Definujte třídu Zlomek, jejíž instance budou mít dva soukromé atributy: čitatel a jmenovatel. Definujte ve třídě sadu metod řešících jednodu-ché operace zlomkové aritmetiky: sčítání, odčítání, násobení a dělenízlomků, resp. zlomku a čísla.

Třída by měla být neměnným hodnotovým typem. Proto nesmějímetody počítající výsledky aritmetických operací vracet upravenou hodnotu své instance či parametru, ale musí vždy vytvořit novou in-stanci a vrátit odkaz na ni. Jedině tak můžete zaručit, že se budou zlomky ve výrazech chovat stejně jako čísla.

Ve svých programech využijte třídu Funkce, konkrétně její statickoumetodu nsn(int,int), která vrací nejmenší společný násobek svých pa-rametrů a statickou metodu nsd(int,int), která vrací největší společný dělitel svých parametrů.

Abyste nemuseli tolik přemýšlet a psát, je pro vás v projektu04_Zlomky připravena třída Zlomek s prázdnými těly metod a třída TestZlomek s příslušnými testy. Vaším úkolem tedy je upravit těla me-tod třídy Zlomek tak, aby všechny připravené testy prošly.

Page 226: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

226 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 226 z 433

Pro ty největší lenochy je v projektu definována i dvojice tříd ZlomekRUP a TestZlomekRUP, které obsahují vzorové řešení a jeho testy. Jestli se ale chcete opravdu něco naučit, pokuste se nejprve vše vyřešit sami a tato řešení použijte pouze pro kontrolu. Pro radu se na ně obracejte opravdu pouze ve stavu nejvyšší-ho zoufalství.

4.13 Shrnutí – co jsme se naučili Třída P obsahuje statické metody zpráva, zadej a souhlas, které umožňují jed-noduché zadávání výstupních zpráv a požadavků na vstupní hodnoty pro-střednictvím dialogových oken.

Operátor je znak nebo skupina znaků naznačujících, že se v daném místě má provést nějaká operace.

V jazyku Java dělíme operátory podle tzv. arity, která označuje, kolik operan-dů daný operátor vyžaduje.

V Javě rozeznáváme operátory unární (s jedním operandem – např. -5), binár-ní (se dvěma operandy – např. 5 + 4) a ternární se třemi operandy (ten jsme ještě neprobírali).

Některé operátory mají přednost před ostatními a vyhodnocují se proto dřív (obdobně jako se v matematice nejprve násobí a pak teprve sčítá). Pořadí vy-hodnocování lze ale vždy upravit pomocí kulatých závorek.

Je-li jeden z operandů operátoru sčítání textový řetězec (String), převede se na řetězec i druhý operand a výsledkem operace je řetězec vzniklý spojením obou řetězců.

Výsledek operace dělení záleží na typu operandů. Dělíme-li celé číslo celým číslem, získáme celou část podílu. Je-li některý z operandů reálný, získáme „přesný“ podíl.

Operátor % vrací zbytek po dělení a je aplikovatelný i na reálná čísla.

Potřebujeme-li změnit typ výsledku operace, můžeme použít operátor přety-pování.

Chceme-li identifikovat instanci nějakým jejím „rodným číslem“, můžeme v atributu třídy načítat počet vytvořených instancí a do konstantního atributu instance uložit vždy při vzniku instance její pořadí.

Page 227: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 4: Dotváříme vlastní třídu 227

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 227 z 433

Pro zvýšení hodnoty číselného atributu o jedničku můžeme použít inkremen-tační operátor ++, pro její snížení dekrementační operátor – –.

Při umístění tohoto operátoru před identifikátor se nejprve provede operace a výsledná hodnota se použije ve výrazu, při umístění za identifikátor se nej-prve použije původní hodnota a teprve pak se upraví.

Pro tisk zpráv na standardní výstup používáme metody print a println ob-jektu out, jenž je veřejným statickým atributem třídy System.

Texty posílané na standardní výstup zobrazuje BlueJ v okně terminálu.

Pošleme-li na standardní výstup znak pro odstránkování (ʹ\fʹ), BlueJ okno terminálu smaže.

Třída může definovat veřejnou metodu toString, která se zavolá vždy, když je potřeba převést příslušnou instanci na řetězec.

Při vytváření nových tříd je výhodné použít šablonu standardní třídy, která má připravené definice často používaných atributů a metod a sadu řádko-vých komentářů specifikujících doporučené pořadí jednotlivých definic.

V testovací třídě chápe systém metody začínající předponou test jako testy, které je schopen na požádání spustit.

Testy můžeme vytvářet nejen automaticky, ale těla testovacích metod může-me upravovat jako jakékoliv jiné programy.

Před spuštěním každého testu se nejprve provede metoda setUp(), která vy-tvoří tzv. přípravek (fixture), což je výchozí sada objektů, se kterou testy pra-cují.

Po ukončení každého testu se provede metoda tearOff(), která má za úkol po vykonaném testu „uklidit“.

Program je možno v kterémkoliv místě zastavit a podívat se na hodnoty jed-notlivých atributů a lokálních proměnných. Slouží k tomu speciální zarážky (breakpoint), které je možno umístit do záhlaví řádku programu.

Když systém při vykonávání programu narazí na zarážku, otevře okno de-buggeru, jež umožní bližší analýzu programu.

Debugger umožňuje program krokovat, a to jak po jednotlivých příkazech metody, v níž se zrovna nachází, tak i vnořením se do útrob metody, která se má právě vykonat.

BlueJ nabízí i možnost, jak předčasně zastavit běžící program.

Page 228: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

228 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 228 z 433

Objektové datové typy můžeme rozdělit na hodnotové a referenční.

Hodnotové datové typy můžeme ještě rozdělit na proměnné a neměnné.

Neměnné datové typy můžeme používat obdobně jako typy primitivní. Mů-žeme např. bezpečně deklarovat veřejné konstanty těchto typů.

Deklarace veřejných konstant proměnných hodnotových typů je velice ne-bezpečná.

Nové termíny

Page 229: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 5: Návrhové vzory 229

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 229 z 433

5. Návrhové vzory

Kapitola 5 Návrhové vzory Opravit

☯ Co se v kapitole naučíme V této kapitole si vysvětlíme, co jsou to návrhové vzory a jaký význam mají při tvorbě programů. Vzápětí si vysvětlíme princip, základní vlastnosti a typické použití prvních vzorů: přepravky a jedináčka. V závěru kapitoly si pak ukážeme, jak je možno definovat výčtové ty-py.

Jednou z důležitých zásad produktivních programátorů je „nevynalézat již vyna-lezené“. Celé objektově orientované programování je „vyladěno“ k tomu, aby při řešení nových úloh programátor nemusel znovu vynalézat nové třídy, ale aby místo toho mohl znovu použít třídy, které naprogramoval někdy dříve.

Snaha o zefektivnění vývoje programů a „nevymýšlení“ dříve vymyšleného jde ještě dále. V posledních letech jsou stále populárnější tzv. návrhové vzory (an-glicky design patterns), které bychom mohli charakterizovat jako návody na řeše-ní některých typických, často se vyskytujících úloh.

Oblibu návrhových vzorů rozpoutalo v roce 1995 vydání knihy Design Pat-terns1, která se stala velice rychle biblí všech objektově orientovaných programáto-rů a povinnou výbavou jejich knihovničky. Kniha obsahuje 23 základních návrhových vzorů všeobecného použití. V počítačové literatuře se autoři na tyto vzory často odkazují, takže se zkušený programátor bez jejich znalosti neobejde2.

1 Gamma E., Helm R., Johnson R., Vlissides J.: Design Patterns: Elements of Reusable Object-Oriented

Software, Addison-Wesley, 1955. Český překlad vydalo v roce 2003 nakladatelství Grada pod názvem Návrh programů pomocí vzorů – Stavební kameny objektově orientovaných programů (ISBN 80-247-0302-5).

2 Autoři této knihy bývají v literatuře často označování jako „gang čtyř“ (anglicky gang of four – ve zkratce GoF). Narazíte-li proto někde na odkaz GoF nebo gang čtyř, jedná se určitě

Page 230: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

230 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 230 z 433

Od té doby byla publikována řada dalších návrhových vzorů, především pak z oblasti vývoje rozsáhlých aplikací běžících na několika počítačích. Nicméně výše zmíněná kniha je všude citována jako základní zdroj prvotních informací z této oblasti.

Převážná většina učebnic programování je ve skutečnosti učebnicemi syntaxe a základního použití vybraného programovacího jazyka, a proto tyto učebnice žádné zmínky o návrhových vzorech neobsahují (případně jen okrajově) a počítají s tím, že se s nimi čtenář ve své další praxi seznámí. Protože se domnívám, že zna-lost nejdůležitějších návrhových vzorů patří k základním znalostem objektového programátora, seznámím vás postupně alespoň s těmi jednoduššími návrhovými vzory.

Ke vzorům popsaným ve výše zmiňované knize GoF přidám i některé další, které sice ve zmiňované knize nejsou (asi je autoři považovali za příliš triviální), avšak které se vám budou hodit.

5.2 Přepravka (Messenger) Hned první ze vzorů, s nimiž vás chci seznámit, v knize GoF není. Tento vzor je v anglické literatuře označován jako Messenger1, což bychom mohli přeložit jako posel nebo kurýr, avšak já mu říkám přepravka. Jeho účelem je totiž umožnit pra-covat s několika hodnotami jako s hodnotou jedinou, a umožnit tak snadné přená-šení této skupiny hodnot mezi jednotlivými instancemi a jejich metodami.

Můžete si představit, že všechny hodnoty naskládáte do přepravky, ve které je předáte tomu, kdo po nich touží. Místo řady jednotlivých hodnot, k nimž se musíte obracet jednotlivě, tak můžete použít jedinou přepravku, v níž máte vše naskládané. Je to obdobné, jako když jdete nakoupit. Také při větším nákupu ne-nesete všechny věci v náručí, ale pořídíte si na ně raději tašku a pro větší nákupy dokonce pojedete autem.

Na rozdíl od tašky, kam pokaždé nahážete něco jiného, bývají přepravky ur-čeny pro předem definovanou sadu hodnot – můžete si je představit např. jako kufřík na nářadí, kde má každý druh nářadí připraven vlastní úchyt.

o odkaz na výše uvedenou knihu. Tuto zkratku budu v odkazech na knihu používat v dal-ším textu i já.

1 Viz např. učebnice Thinking in Patterns stáhnutelná zdarma z adresy www.bruceeckel.cz.

Page 231: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 5: Návrhové vzory 231

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 231 z 433

Třída, jejíž instance mají sloužit jako přepravky předem známé sady několika hodnot, by měla mít následující vlastnosti:

Pro každou přenášenou hodnotu bude definován atribut, do kterého se pak tato hodnota uloží.

Protože účelem třídy není hodnoty atributů svých instancí schovat, ale pouze je shromáždit, aby se snadněji přenášely z místa na místo, deklarují se jejich atributy většinou jako veřejné. To s sebou ovšem nese povinnost dávat pozor na to, abychom tyto atributy náhodou zvenku nepřípustným způsobem neo-vlivnili.

S prvními třídami, které jsou implementací tohoto vzoru, jste se seznámili již v pasáži Kvalifikace atributů na straně 134. Instance tříd Pozice a Rozměr nebyly ničím jiným než takovými přepravkami. V této kapitole k nim přidáme ještě třídu Oblast, jejíž instance uchovávají informace o pozici a rozměru příslušné oblasti.

Správná přepravka však není pouze schránkou na předávané hodnoty, ale bývá zároveň doplněna přístupovými metodami ke svým atributům. Její atributy sice jsou veřejné, ale definicí přístupových metod danou třídu sjednotíme s běž-nými zvyklostmi, takže kdo bude chtít, bude moci přistupovat k jejím atributům stejně, jako by přistupoval k soukromým atributům standardních tříd.

Vedle metod, které přímo nastavují hodnoty jednotlivých atributů, bývají tří-dy sloužící jako přepravky dovybaveny metodami, které do jejich atributů kopíru-jí hodnoty atributů instance předávané jako parametr. A při té příležitosti jsou také řady jejich konstruktorů rozšířeny o tzv. kopírovací konstruktor, který vy-tváří novou instanci jako kopii instance předané jak parametr.

O tom, co by měla přepravka obsahovat, jsem se již navyprávěl dost. Pokuste se nyní definovat třídu Oblast. Nezapomeňte jí definovat kopí-rovací konstruktor a přístupové metody. Umožněte v nich předávat ja-ko parametry nejenom čísla a instance třídy Oblast, ale i instance tříd Pozice a Rozměr.

Tak co, hotovo? Svůj program si můžete porovnat se vzorovým řešením v projek-tu 05_vzory. V něm si pak můžete prohlédnout i plné definice tříd Pozice a Rozměr.

Abychom mohli definované přepravky plně využít, je vhodné rozšířit mno-žinu konstruktorů a metod našich grafických tříd o verze, které budou přebírat ty-to přepravky jako své parametry, a o verze přístupových metod, které budou takovéto přepravky přebírat a vracet.

V projektu 05_vzory jsem základní grafické třídy o tyto konstruktory a metody doplnil. V následujícím výpisu si můžete prohlédnout, jak lze jednoduše doplnit

Page 232: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

232 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 232 z 433

třídu Strom o metody pro čtení a nastavování pozice instance a o metody pro zjiš-ťování jejího rozměru a zabrané plochy.

Asi vás napadlo, proč chybějí metody pro nastavování plochy. Je to proto, že jsme ještě nedefinovali metodu setRozměr(int,int), protože od chvíle, kdy jsme zavedli možnost zadat poměr výšky, resp. šířky kmene a celého stromu, se stala její definice trochu složitější a vyžaduje změny v doposud definované části třídy. Nechal jsem si ji proto do následující kapitoly, kde si na ní ukážeme, jak při tako-vých „vynucených“ změnách programu postupovat. 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

// ... Předchozí část definice třídy //== KONSTRUKTORY A TOVÁRNÍ METODY ============================================ /************************************************************************** * Vytvoří novou instanci se zadanou polohou a rozměry * a implicitní barvou. * * @param počátek Pozice počátku instance * @param rozměr Rozměr instance */ public Strom(Pozice počátek, Rozměr rozměr) { this( počátek.x, počátek.y, rozměr.šířka, rozměr.výška ); } /************************************************************************** * Vytvoří novou instanci vyplňující zadanou oblast * a mající implicitní barvu. * * @param oblast Oblast definující pozici a rozměr instance */ public Strom(Oblast oblast) { this( oblast.x, oblast.y, oblast.šířka, oblast.výška ); } //== PŘÍSTUPOVÉ METODY ATRIBUTU INSTANCÍ ====================================== /************************************************************************** * Vrátí instanci třídy Pozice s pozicí instance. * * @return Pozice s pozicí instance. */ public Pozice getPozice() { return new Pozice( xPos, yPos ); } /************************************************************************** * Nastaví novou pozici počátku instance. *

Page 233: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 5: Návrhové vzory 233

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 233 z 433

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

* @param pozice Nová pozice instance */ public void setPozice(Pozice pozice) { setPozice( pozice.x, pozice.y ); } /************************************************************************** * Vrátí instanci třídy Rozměr s rozměry instance. * * @return Rozměr s rozměry instance. */ public Rozměr getRozměr() { return new Rozměr( šířka, výška ); } /************************************************************************** * Vrátí instanci třídy Oblast s informacemi o pozici a rozměrech instance. * * @return Oblast s informacemi o pozici a rozměre instance. */ public Oblast getOblast() { return new Oblast( xPos, yPos, šířka, výška ); } // ... Následující část definice třídy

Jak vidíte, začlenění práce s přepravkami do programu je opravdu jednoduché. Od této chvíle může jeden obrazec požádat např. o instanci pozice a pak tuto in-stanci předat dalším obrazcům tak, aby si všechny mohly dát na daném místě „rande“.

5.3 Jedináček (Singleton) Některé třídy potřebujeme definovat tak, aby uživatel nemohl svobodně ovlivňo-vat počet jejich instancí. Typickým příkladem je např. třída Plátno, o které jsme si již několikrát řekli, že trvá na tom, aby její instance byla jedináček. Chceme-li totiž zabezpečit, aby se všechny grafické obrazce malovaly na jedno a to samé plátno, musíme zařídit, aby další plátno nebylo možné žádným způsobem vytvořit, pro-tože jinak bychom museli definovat nějaké pravidlo, podle nějž se budou instance rozhodovat, ke kterému plátu patří.

Page 234: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

234 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 234 z 433

Vytvoření třídy, která bude mít jedinou instanci, popisuje návrhový vzor označovaný v anglické literatuře jako singleton, což bychom mohli do češtiny přeložit jako jedináček.

Třída, jejíž instance má být jedináček (případně má mít předem definovaný počet instancí, ale o tom až za chvíli), musí splňovat tři podmínky:

Definovat konstruktor jako soukromý, aby jej nemohl nikdo jiný zavolat vy-tvořit tak další instanci.

Deklarovat odkaz na instanci, která bude jedináčkem, jako atribut třídy, jenž bude hned v deklaraci inicializován zavoláním konstruktoru (deklarace tak bude zároveň definicí). (Časem se naučíme ještě jiné způsoby definice jedi-náčka.)

Definovat metodu třídy (statickou metodu), která na požádání poskytne od-kaz na tohoto jedináčka.

Základní rozdíl mezi konstruktorem a metodou vracející odkaz na instanci je v tom, že konstruktor musí po svém zavolání vytvořit novou instanci (musí ji zkonstruovat – proto se tak také jmenuje), kdežto obyčejná metoda se může svo-bodně rozhodnout, zda vytvoří instanci novou nebo vrátí odkaz na instanci exis-tující.

Teoreticky by bylo možné se zavedení takovéto metody vyhnout a de-finovat jedináčka jako veřejnou statickou konstantu dané třídy, jenžetím by konstruktér třídy zbytečně odhaloval podrobnosti o způsobuimplementace a porušoval by tak pravidlo o jejím zapouzdření. Těžkoby se pak např. mohl v budoucnu rozhodnout změnit definici třídy tak, aby místo jediné instance využívala služeb několik instancí. Jakmile se totiž něco jednou objeví v rozhraní třídy (tj. mezi jejími veřejnými čle-ny), kterou začnou používat jiné třídy, to už tam musí zůstat.

Page 235: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 5: Návrhové vzory 235

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 235 z 433

Abyste si tvorbu jedináčka vyzkoušeli, nadefinujte třídu ČernáDíra, kte-rá definuje ve středu plátna malý černá kruh. Metodu, která bude vra-cet odkaz na jedináčka, nazvěte getČernáDíra.

Pocvičte si navíc svoji logiku a pokuste se definovat sérii metod spolkni(X), jejichž parametry budou postupně všechny tři základní tva-ry, tj. Elipsa, Obdélník a Trojúhelník. Tato metoda na třikrát (tj. na tři posuny s drobným oddechem mezi nimi) přitáhne objekt, který pře-vezme jako parametr, a když bude střed objektu nad středem černé dí-ry, tak jej na třikrát spolkne, tj. třikrát jej vždy zmenší a po třetímzmenšení jej odmaže.

Mezi jednotlivými přesuny, resp. zmenšeními počkejte vždy 0,5sekundy prostřednictvím zavolání metody P.čekej(milisekund).

Tentokrát vás výjimečně nebudu nutit k tomu, abyste třídu definovali sami. Bu-de-li se vám zdát, že jste způsob implementace jedináčka zcela nepochopili, podí-vejte se do následujícího vzorového řešení.

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 30 31 32

/******************************************************************************* * Třída ČernáDíra slouží k ukázce definice třídy, * jejíž instance má zůstat jedináčkem. * * @author Rudolf Pecinovský * @version 2.01, duben 2004 */ public class ČernáDíra { //== KONSTANTNÍ ATRIBUTY TŘÍDY ================================================= /** Průměr kruhu tvořícího černou díru. */ private static final int PRŮMĚR = 10; private static final int POLOMĚR = PRŮMĚR / 2; /** Definuje jedináčka jako konstnatní atribut třídy a hned mu také * přiřazuje počáteční hodnotu. */ private static final ČernáDíra jedináček = new ČernáDíra( PRŮMĚR ); //== KONSTANTNÍ ATRIBUTY INSTANCÍ ============================================== /** Odkaz na kruh, který na plátně představuje danou díru. */ private final Elipsa díra; //== PŘÍSTUPOVÉ METODY ATRIBUTŮ TŘÍDY ==========================================//##############################################################################//== KONSTRUKTORY A METODY VRACEJÍCÍ INSTANCE VLASTNÍ TŘÍDY ==================== /*************************************************************************** * Metoda vracející odkaz na jedináčka.

Page 236: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

236 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 236 z 433

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

*/ public ČernáDíra getČernáDíra() { return jedináček; } /*************************************************************************** * Implicitní konstruktor třídy ČernáDíra */ private ČernáDíra( int průměr ) { Plátno plátno = Plátno.getPlátno(); int x = plátno.getŠířka() / 2 - POLOMĚR; int y = plátno.getVýška() / 2 - POLOMĚR; díra = new Elipsa( x, y, PRŮMĚR, PRŮMĚR, Barva.ČERNÁ ); } //== NOVĚ ZAVEDENÉ METODY INSTANCÍ ============================================= /*************************************************************************** * Přesune zadanou instanci nad sebe a vcucne ji, tj. zmenší ji postupně * až na nulovou velikost. Přesun i spolknutí provede vždy ve třech krocích. * * @param elipsa Polykaná elipsa. */ public void spolkni( Elipsa elipsa ) { int xe = elipsa.getX(); int ye = elipsa.getY(); int se = elipsa.getŠířka(); int ve = elipsa.getVýška(); int xd = díra.getX() + POLOMĚR; int yd = díra.getY() + POLOMĚR; int dx = (xd - (xe + se/2)) / 3; int dy = (yd - (ye + ve/2)) / 3; elipsa.setPozice( xe = xe + dx, ye = ye + dy ); P.čekej( 500 ); elipsa.setPozice( xe = xe + dx, ye = ye + dy ); P.čekej( 500 ); elipsa.setPozice( xe = xe + dx, ye = ye + dy ); P.čekej( 500 ); dx = se / 6; dy = ve / 6; elipsa.setPozice( xe += dx, ye += dy ); elipsa.setRozměr( se -= 2*dx, ve -= 2*dy ); P.čekej( 500 ); elipsa.setPozice( xe += dx, ye += dy ); elipsa.setRozměr( se -= 2*dx, ve -= 2*dy ); P.čekej( 500 ); elipsa.setPozice( xe += dx, ye += dy ); elipsa.setRozměr( se -= 2*dx, ve -= 2*dy ); díra.nakresli(); P.čekej( 500 );

Page 237: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 5: Návrhové vzory 237

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 237 z 433

91 92 93 94 95 96 97 98 99

100 101 102 103 104 105 106 107 108 109 110 111

elipsa.smaž(); díra.nakresli(); } //== TESTY A METODA MAIN ======================================================= /*************************************************************************** * Testovací metoda. */ public static void test () { Elipsa e = new Elipsa(); jedináček.spolkni( e ); e = new Elipsa( 200, 200, 100, 50 ); jedináček.spolkni( e ); } }//public class ČernáDíra

5.4 Výčtové typy

5.5 Shrnutí – co jsme se naučili Návrhové vzory jsou doporučená řešení často se vyskytujících úloh. Jejich hlavním účelem je zefektivnit práci návrháře, který tak nemusí vymýšlet ře-šení, ale může pouze aplikovat osvědčený vzor.

Návrhový vzor Přepravka (Messenger) ukazuje, jak postupovat v případě, kdy potřebujeme pracovat s několika hodnotami jako s celkem. Podle něj de-finujeme třídu (přepravku), která bude mít pro každou z těchto hodnot vy-hrazen atribut. Oproti běžným zvyklostem budou atributy přepravky deklarovány jako veřejné.

Jedináček (singleton) je třída, která povoluje vytvoření pouze jediné instance. Odkaz na tuto instanci bude mít uložen v atributu třídy, který bude iniciali-zován hned v deklaraci atributu zavoláním příslušného konstruktoru. Třída nesmí definovat veřejný konstruktor. Konstruktor musí být soukromý a k získání odkazu na jedináčka slouží zvláštní metoda.

Page 238: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

238 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 238 z 433

Výčtový datový typ použijeme v případě, kdy budeme potřebovat definovat konečnou sadu hodnot (příkladem jsou např. směry).

Nové termíny

Page 239: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 239 z 433

Část 2: Více tváří

Page 240: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

240 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 240 z 433

6. Rozhraní

Kapitola 6 Rozhraní

☯ Co se v kapitole naučíme V této kapitole se seznámíme s novou vlastností tříd a jejich instancí. Označujeme ji jako polymorfizmus (mnohotvárnost) a vyjadřujeme jí skutečnost, že objekty se mohou v různých situacích vydávat za instan-ce různých typů.

Seznámíme se se zvláštním druhem třídy nazývaném rozhraní a ukážeme si, v jakých situacích je výhodné rozhraní používat a co jeho zavedení našim programům přináší. Vysvětlíme si jak třídy rozhraní implementují a předvedeme si, že jedna třída může implementovat i několik rozhraní.

Seznámíme se také s novým návrhovým vzorem Služebník. V závě-ru kapitoly si v závěrečném projektu vyzkoušíme aplikaci získaných dovedností na příkladu grafické simulace problémů z „negrafického“ světa – zkusíte si naprogramovat simulaci výtahu.

S novým tématem otevřeme i nový projekt, kde některé třídy přibudou, jiné budou chybět a definice ostatních budou alespoň upravené. Projekt pro tuto kapitolu se jmenuje 06_Rozhraní_A a stejně jako u minulého pro-jektu má i on svého partnera 06_Rozhraní_Z, který bude obsahovat třídy, jejichž definice v průběhu lekce upravíme či přidáme.

Ukažme si projevy polymorfizmu na příkladu ze života: jdete-li do restaurace, ob-sluhuje vás číšník. Kdybychom takovouto situaci programovali, pak by v našem programu vystupovali (mimo jiné) dva objekty: vy jako instance třídy Zákazník a obsluhující jako instance třídy Číšník. I číšníci však mají občas volno a mohou si pak dojít do restaurace. Pak ale vystupují jako instance třídy Zákazník a obsluhuje je jiná instance třídy Číšník.

Page 241: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 241

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 241 z 433

Obdobných situací bychom v životě našli celou řadu. V této kapitole si uká-žeme její ekvivalent ve světě grafických objektů a pláten, na která se tyto objekty kreslí.

6.1 Kreslíme jinak V našich dosavadních příkladech jsme se smiřovali s tím, že přesouvající se tvary smazaly ve své původní pozici nejenom sebe, ale i části tvarů, které zasahovaly pod ně či na ně. Pokud jsme chtěli nějaký umazaný nebo dokonce smazaný tvar vidět, museli jsme jej explicitně požádat o to, aby se překreslil. Kreslené objekty jeden o druhém nevěděly a ani plátno nevědělo nic o tom, co je na něm nakresle-no.

V této kapitole naše kreslící možnosti trochu vylepšíme. Opustíme třídu Plátno, kterou jsme používali doposud, a přestaneme říkat našim obrázkům, aby se nakreslily. Místo toho začneme používat třídu AktivníPlátno, která se o nakres-lení našich obrázků na plátno postará sama. tj. sama je ve vhodnou chvíli požádá, aby se nakreslili.

Třída AktivníPlátno obrací celou naši dosavadní filozofii kreslení obrázků naruby. Doposud jsme to dělali tak, že když jsme chtěli objekt nakreslit, požádali jsme třídu Plátno o nějaké plátno, tomu jsme nastavili kreslící barvu a nařídili mu, aby náš objekt touto barvou nakreslilo (podívejte se, jak byly v třídách Elipsa, Obdélník či Trojúhelník definovány metody nakresli()).

Třída AktivníPlátno na to však jde úplně jinak. Její instance není pouze pa-sivním objektem, jenž nám umožňuje kreslit na obrazovku, ale chová se spíše jako manažer, který dostane nějaké objekty do správy a dohlíží na to, aby byly všechny správně nakresleny. Ví, které objekty jsou na plátně, a vždy, když se dozví, že se něco změnilo, tak zařídí, aby obrázek na plátně odpovídal skutečnosti.

Jako správný manažer ale instance třídy AktivníůPlátno samozřejmě nepra-cuje, tj. nic nekreslí – nakreslit se musejí jednotlivé objekty samy. Aktivní plátno pouze celé kreslení organizuje. Řekne objektu, kdy se má nakreslit a předá mu kreslítko, kterým se pak nakreslí.

Aby se třída mohla takto chovat, potřebuje vědět dvě věci:

které objekty se mají na plátně zobrazovat,

kdy se stav některého z nakreslených objektů změnil natolik, že je třeba plát-no překreslit.

Page 242: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

242 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 242 z 433

Jak jsem již řekl, nyní již nebudeme našim objektům říkat, aby se nakreslily. Bu-deme-li chtít, aby byl náš objekt na plátně nakreslen, požádáme třídu AktivníPlátno, aby jej zařadila mezi objekty, o jejichž vykreslení se stará.

Kdykoliv se nyní na plátně cokoliv změní, tak AktivníPlátno postupně požá-dá všechny spravované objekty, aby se překreslily. Požádá-li je o to ve správném pořadí, budou objekty bez problému přes sebe přejíždět a pod sebou podjíždět a v každém okamžiku bude vše správně nakresleno.

Aby mohla instance třídy AktivníPlátno takto pracovat, musíme jí slíbit, že náš objekt bude schopen se na požádání nakreslit. Přesněji, že bude mít ve své „sbírce“ metodu, jež bude schopna převzít od třídy AktivníPlátno kreslítko (ob-jekt třídy java.awt.Graphics2D) a s jeho pomocí se nakreslit.

Asi nyní namítnete, že v názvu třídy java.awt.Graphics2D jsou tečky, o kterých jsem se při výkladu pravidel pro tvorbu identifikátorů ne-zmínil (viz pasáž Pravidla pro tvorbu identifikátorů v jazyce Java na straně 46). Tyto tečky nejsou součástí vlastního identifikátoru třídy, ale slouží pouze k oddělení částí popisujících, jak má virtuální stroj najít danoutřídu v knihovně. Podrobněji si o těchto záležitostech budeme povídatv kapitole Budete si to přát zabalit? na straně 376.

Kreslítko, jež aktivní plátno předává objektu, který žádá o nakreslení se, je záro-veň vynikajícím zabezpečovacím prostředkem, který nám dokáže zaručit, že se nikdo jiný na plátno nenakreslí, tj. že se nikdo nenakreslí, aniž by byl požádán ak-tivním plátnem. Kdo nedostane kreslítko, ten se nemůže nakreslit. Jediný, od ko-ho lze kreslítko získat, je aktivní plátno, a to je předá vždy pouze tomu objektu, který se podle něj má v danou chvíli nakreslit.

6.2 Rozhraní jako zvláštní druh třídy Jistě vás zajímá, jak takový slib realizujeme. Pomůžeme si zvláštním druhem třídy označované jako rozhraní (interface). Tento termín jsme již zavedli, když jsme hovořili o zapouzdření (viz podkapitola Zapouzdření na straně 128) a o dokumen-taci (viz podkapitola Komentáře a dokumentace na straně 148). Tehdy jsme si řekli, že „rozhraní třídy budeme chápat jako množinu informací, které o sobě třída zve-řejní“. Způsob, jakým je dané rozhraní naprogramováno jsme označili jako im-plementaci.

Tato terminologie zůstane v platnosti i v okamžiku, kdy budeme hovořit o rozhraní jako o zvláštním druhu třídy. Rozhraní totiž na rozdíl od obyčejných tříd

Page 243: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 243

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 243 z 433

neříká vůbec nic o tom, jak budou jednotlivé metody implementovány. Pouze de-klaruje seznam veřejných metod, které budou jeho „instance“ implementovat. Ji-nými slovy: deklaruje, na jaké zprávy budou umět jeho „instance“ reagovat.

V našem novém projektu je např. definováno rozhraní IKreslený. Jeho defini-ce vypadá (bez oddělovacích komentářů) následovně: 1 2 3 4 5 6 7 8 9

10 11

public interface IKreslený { /*************************************************************************** * Za pomoci dodaného kreslítka vykreslí obraz své instance * na animační plátno. * * @param kreslítko Kreslítko, kterým se instance nakreslí na plátno. */ void nakresli( java.awt.Graphics2D kreslítko ); }//public interface IKreslený

V tomto kurzu budu všechna rozhraní označovat identifikátory začína-jícími na I. Přiznám se, že jsem tento zvyk převzal z dob, kdy jsem ještěprogramoval v C++, kde se nejrůznější předpony používaly poměrněčasto. V javové komunitě sice zdobení identifikátorů prefixy nebývá zvykem, ale podle mne je pro začátečníky výhodné, když na první po-hled poznají, zda se jedná o identifikátor třídy nebo rozhraní. Proto sev tomto textu setkáte s podobnými „návodnými značeními“ ještě něko-likrát.

V definici rozhraní si všimněte dvou odchylek od definice standardní třídy:

V hlavičce třídy je místo klíčového slova class použito klíčové slovo interface, jímž překladači oznamujeme, že nepřekládá standardní třídu ale rozhraní.

V těle rozhraní je zapsána pouze hlavička metody ukončená středníkem. Tělo metody je totiž záležitostí implementace a jak jsme si již řekli, o tu se rozhraní nestará.

Ti bystřejší z vás si možná ještě všimli toho, že u metody není uveden modifikátor public. Je to proto, že všechny metody deklarované v rozhraní jsou automaticky veřejné. Překladač proto na explicitním (=veřejném, zjevném) zapsání tohoto mo-difikátoru netrvá. Když jej ale uvedete, nic tím nepokazíte. Pokazit byste mohli program pouze tím, že byste zde uvedli jiný modifikátor než public.

Page 244: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

244 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 244 z 433

6.3 Instance rozhraní V první kapitole jsme si říkali, že se všechny objekty dělí do tříd. V další kapitole jsme si prozradili, že každá třída musí obsahovat konstruktor, což je předpis, podle nějž se vytvářejí její instance. Jakýkoliv definovaný postup, tedy i konstruk-tor, ale nutně patří do implementace. Nemá-li rozhraní obsahovat žádnou imple-mentaci, nemůže obsahovat ani konstruktor a tím pádem nemůže být možno vytvořit jeho instanci.

Řeknete si asi: „K čemu nám tedy může být dobré, když nemůžeme vytvořit jeho instanci?“ Rozhraní sice svou instanci vytvořit nemůže, ale Java dovoluje, aby se za jeho instance vydávaly instance „obyčejných“ tříd.

Takováto třída se ale musí veřejně přihlásit k tomu, že bude dané rozhraní implementovat, aby se to o ní překladač dozvěděl a mohl zkontrolovat, že oprav-du implementuje všechny metody, které dané rozhraní deklaruje. Odměnou jí za to bude možnost vydávat svoje instance za instance implementovaného rozhraní. (Umějí vše, co rozhraní slibovalo, že jeho instance budou umět, takže jim to nedě-lá potíže.)

Chcete-li pro rozhraní příklad z lidské společnosti, mohli bychom říct, že rozhraní je takový ideolog. Vyhlásí, jak by se něco mělo dělat, a nechá na svých stoupencích, aby jeho „myšlenky“ realizovali (=implementovali).

Kdybychom se znovu vrátili k naší analogii se světem robotů a jejichvozidel-instancí, tak bychom mohli rozhraní považovat za něco jako li-cenci pro instance. Chce-li továrna vyrábět vozidla s osádkami, které mohou pracovat např. jako malíři, požádá zastupitelstvo o licenci (de-klaruje implementaci rozhraní) a prokáže-li splnění všech požadova-ných podmínek (tj. přítomnost robotů-metod, kteří zvládají činnosti požadované pro daný druh licence), mohou její vozidla přijímat od ostatních instancí zakázky pro licencovanou činnost – v našem případě pro malíře.

6.4 Nový projekt Tak dost již teorie a pojďme se podívat, jak bychom jak se taková implementace rozhraní třídou řeší v praxi.

Page 245: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 245

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 245 z 433

Otevřete si projekt 06_Rozhraní_A a pojďte se nejprve podívat, jak implementace rozhraní projeví v diagramu tříd. Po prvním otevření by měl projekt vypadat jako na obr. 6.1.

Jak vidíte, výchozí projekt nám oproti minulé kapitole zase trochu zesložitěl. Vedle toho, že třídu Plátno nahradila třída AktivníPlátno, tak v něm ještě přibyly třídy Čára a Text a především pak rozhraní IKreslený.

Všimněte si, že rozhraní je v diagramu tříd reprezentováno trochu jinak než běžná třída:

Obrázek 6.1 Podoba projektu 06_Rozhraní_A po prvním otevření

Page 246: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

246 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 246 z 433

Ti, kteří aplikovali konfiguraci RUP, si především všimnou, že rozhraní je ze-lené (ve standardní konfiguraci má rozhraní stejnou barvu jako běžné třídy).

V horní části obdélníku reprezentujícího rozhraní není pouze název rozhraní, ale nad ním je ještě text «interface», který slouží k rozpoznání rozhraní i v případě, kdy jeho ikona není od ikon běžných tříd barevně odlišena.

Text uzavřený ve «francouzských uvozovkách» (zdvojených špičatýchzávorkách) je v jazyku UML označován jako stereotyp a slouží k bližší specifikaci typu daného objektu – v našem případě rozhraní.

Podíváte-li se do složky s projektem, zjistíte, že z hlediska vytvořených souborů se rozhraní chová jako obyčejná třída – i ono má definovánosvůj zdrojový soubor s příponou java a přestože nemá definovánu im-plementaci, při jeho překladu se vytvoří class-soubor s příponou class.

Počet tříd v projektu je již tak velký a vzájemné vazby tak hojné, že už byl docela problém uspořádat třídy tak, aby šipky jejich závislostí byly ještě relativně sledo-vatelné.

Řekl bych, že zrovna v tomto projektu jsou vzájemné vazby natolik zřejmé, že bychom se jistě obešli i bez toho, že by byly podrobně vykreslené. Toho se dá do-sáhnout poměrně snadno: stačí v nabídce Zobrazit zrušit volbu Zobrazit "Používá" (obr. 6.2).

Obrázek 6.2 V nabídce Zobrazit je možno zrušit volbu Používá a nastavit volbu Dědičnost

Po zrušení volby zmizí všechny čáry. Při rušení jste si však mohli všimnout, že pod volbou Zobrazit "Používá" je volba Zobrazit "Dědičnost". Po nastavení této volby se

Page 247: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 247

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 247 z 433

zobrazí šipky s trojúhelníkovou hlavičkou, které vedou od tříd, jež implementují rozhraní IKreslený k tomuto rozhraní (viz obr. 6.3).

To je ale informace, která pro nás má v daném okamžiku daleko větší vypo-vídací hodnotu, než šipky závislostí, jejichž zobrazování jsme před chvílí zrušili, protože nyní např. poznáme, které instance můžeme přihlásit do správy aktivního plátna.

Uspořádání tříd na pracovní ploše, které vycházelo ze snahy o maximálně přehledné zobrazení vzájemných závislostí, ale nyní působí poněkud chaoticky. Nic nám však nebrání třídy přeuspořádat např. podle obr. 6.4. Jak vidíte, diagram se výrazně zpřehlednil.

Obrázek 6.3 Diagram tříd po zrušení zobrazování závislostí a nastavení zobrazování dědičnosti

Page 248: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

248 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 248 z 433

Práce s novým plátnem Než si začneme vysvětlovat, jak zařídíme, aby se instance našich tříd mohly vy-dávat za instance daných rozhraní, ukážeme si nejdříve, jaké změny potkaly náš projekt po zavedení aktivního plátna. Zkusíme si proto několik jednoduchých operací:

1. První změna je, že při práci s objekty na plátně potřebujeme mít v zásobníku odkazů odkaz na aktivní plátno. Proto začněte tím, že třídě AktivníPlátno pošlete zprávu getPlátno(), která vám vrátí odkaz na plátno. Ten uložte do zásobníku odkazů a nazvěte jej třeba AP.

Při té příležitosti si všimněte, že plátno v aplikačním okně je rozděleno pravidelnou sítí čar. Ta nám umožní lepší orientaci v souřadnicích.

Obrázek 6.4 Přeuspořádaný diagram tříd

2. Pomocí implicitního konstruktoru vytvořte instanci obdélníka.

3. Vytvořený obdélník se ale na plátně neobjeví. Nikdo jej totiž ještě do správy plátna nepřihlásil. Učiňte tak – pošlete plátnu zprávu přidej(obrazec), které jako parametr zadáte vytvořený obdélník.

Page 249: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 249

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 249 z 433

4. Obdélník se objeví na v levém horním rohu plátna.Všimněte si, že vytvořený obdélník bude zabírat právě dvě vodorovně sousedící pole plátna.

Kromě toho plátno BlueJ otevře dialogové okno, v němž oznámí, že plát-no vrátilo logickou hodnotu true, což (jak si můžete přečíst v dokumentaci) znamená, že plátno dotyčný obrazec ve své správě ještě nemělo a přidalo jej mezi spravované.

5. Obdobným způsobem vytvořte a poté přihlaste „implicitní elipsu“.

6. Obdobně vytvořte a přihlaste i „implicitní trojúhelník“.

Pokud jste dělali vše tak, jak jsem říkal, měly by výsledná podoba plátna nyní od-povídat obr. 6.5. Zkuste nyní s plátnem a obrazci na něm trochu experimentovat:

Obrázek 6.5 Implicitní velikost obrazců je odvozena z velikosti polí aktivního plátna

Pohybujte jednotlivými vytvořenými obrazci (tj. volejte jejich posunové me-tody) a ověřte, že se obrazce nepřemazávají a že dokonce při přesunech za-chovávají vzájemnou „hloubku“, tj. že obdélník bude vždy spodní a naopak trojúhelník vždy horní.

Zároveň si všimněte, že bezparametrické přesuny posouvají obrazce právě o velikost jednoho pole.

Požádejte plátno, aby změnilo svůj rozměr. U aktivního plátna však již nebu-de zadávat rozměr v bodech, ale v polích. Požádejte jej, aby nastavilo šířku na 4 políčka a výšku na 3 políčka. Všimněte si, že po změně rozměru plátna zů-stávají obrazce stále nakresleny.

Page 250: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

250 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 250 z 433

Nastavte nyní plátnu velikost kroku např. na 10 bodů a všimněte si, jak se podle velikosti kroku změní vzdálenost čar mřížky.

Zkuste znovu pohybovat obrazci a ověřte, že bezparametrické posunové me-tody stále posunují obrazci o velikost jednoho pole.

Projděte dokumentaci všech tříd a vyzkoušejte nové metody a ověřte, nakolik jejich chování dopovídá očekávanému.

Událostmi řízené programování Tady bych vás chtěl upozornit na jednu drobnost. Předpokládám, že jste sivšimli, že instance, která je předána do péče aktivního plátna, vůbec neví,kdy bude požádána o to, aby se nakreslila. Vy předáte objekt aktivnímu plát-nu a vesele si dále řešíte svůj program a necháváte na aktivním plátnu, abyvaší instanci ve vhodnou chvíli oznámilo, že se má překreslit.

Takovýto způsob řešení problémů bývá označován jako událostmi říze-né programování. Instanci přihlášenou u aktivního plátna bychom mohlioznačit za posluchače, který naslouchá dění a čeká, až nastane ta správnáudálost.

Tato událost nastane v okamžiku, kdy se aktivní plátno dozví, že se mápřekreslit. Je přitom úplně jedno, jestli se bude překreslovat proto, že se něja-ký objekt na plátně posunul a řekl plátnu, že se musí překreslit, protože jeho obsah již vypadá jinak, nebo jestli plátno požádal o překreslení operační sys-tém, protože jste se rozhodli okno s plátnem posunout na obrazovce o kousekvedle a vše se proto musí nakreslit v nové pozici.

Je jedno, jak došlo k události vyvolávající překreslení plátna. Plátno za-reaguje vždy stejně: oznámí jednotlivým posluchačům, že k události došlo, aže oni mají udělat to, kvůli čemu se přihlásili – překreslit se. A protože aktiv-ní plátno umí požádat své posluchače vždy ve správném pořadí, bude obsah vždy správně vykreslen.

6.5 Implementace rozhraní Tak jsme si pohráli a měli bychom zase začít trochu programovat. Přidejte do pro-jektu třídu Strom z projektu 05_Vzory (v diagramu tříd na ni máme vlevo dole při-pravené místo).

Page 251: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 251

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 251 z 433

Nyní se pokusíme tuto třídu přizpůsobit podmínkám nového projektu. Nej-prve zařídíme, aby se třída přihlásila k implementaci rozhraní IKreslený. Máme dvě možnosti jak to zařídit:

Vytvořit implementační šipku v diagramu tříd.

Zapsat informaci o deklarovaném rozhraní přímo do hlavičky třídy.

Implementace rozhraní v diagramu tříd Začneme tou jednodušší, kterou je natažení implementační šipky v diagramu tříd. Postup je velmi jednoduchý:

1. Klepněte v levém panelu na tlačítko Vytvořit vztah "Dědičnost" se šipkou s trojú-helníkovou hlavičkou (viz obr. 6.6).

Obrázek 6.6 První krok při natahování šipky dědičnosti

2. V dolním okraji okna projektu se objeví text Vyberte podtřídu (dceřinou třídu), která dědí, resp. implementuje. Poslechněte jej a klepněte na třídu Strom.

3. V dolním okraji okna projektu se objeví text Vyberte nadtřídu (rodičovskou třídu, roz-hraní), ze které se dědí. Opět uposlechněte a klepněte na rozhraní IKreslený. Všimněte si přitom, že při pohybu myší za ní BlueJ hned natahuje budoucí šipku.

Jak prosté, že?

Někomu bude možná lépe vyhovovat, když po stisku tlačítka Vytvořitvztah "Dědičnost" najede na implementující třídu, tam stiskne tlačítko my-ši, se stisknutým tlačítkem přejede na implementované rozhraní, kde tlačítko myši pustí.

Page 252: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

252 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 252 z 433

Kdybyste později dospěli k závěru, že třída dané rozhraní implementovat nemá, stačí klepnout pravým tlačítkem na implementační šipku. Šipka pak „ztloustne“ a objeví se u ní místní nabídka s jediným příkazem: Odstranit. Jeho zadáním šipku zrušíte (vyzkoušejte si to).

Implementace rozhraní ve zdrojovém kódu Přímý zápis do kódu není o nic složitější. Ukážeme si, jak to BlueJ zařizuje za nás. Protože jsme před právě odstranili implementační šipku, můžeme si celý proces ukázat od začátku.

1. Otevřete zdrojový kód třídy Strom a upravte jeho rozměr a polohu tak, aby se vám okno editoru nepřekrývalo s oknem projektu.

2. Najeďte posuvníkem tak, abyste v okně editoru viděli hlavičku třídy.

3. Natáhněte v okně projektu implementační šipku od třídy Strom k rozhraní IKreslený. Všimněte si, že BlueJ okamžitě doplnil do hlavičky „implementač-ní dovětek“, takže hlavička má nyní tvar:

public class Strom implements IKreslený

4. Odstraňte implementační šipku a všimněte si, že „implementační dovětek“ z hlavičky zase zmizel.

5. Zkuste to nyní obráceně a dopište implementační dovětek do hlavičky třídy „ručně“.

6. Stiskem CTRL+S zdrojový kód uložte a všimněte si, že BlueJ okamžitě doplnil do diagramu tříd implementační šipku.

7. Smažte „implementační dovětek“ a přesvědčte se, že po následném uložení souboru implementační šipky opět zmizela.

8. Na závěr svého experimentování implementační šipku opět vytvořte.

6.6 Úprava zdrojového kódu třídy Strom

Třída Strom se tedy přihlásila k implementaci rozhraní IKreslený. Nyní už nám zbývá pouze upravit její zdrojový text tak, aby jej byl překladač ochoten přeložit. Pojďme jej tedy společně upravit.

Page 253: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 253

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 253 z 433

☯ V této kapitole s vámi půjdu při úpravách kódu krok za krokem, abystepoznali, jak takový proces většinou probíhá. Dopředu upozorňuji, že to nebude jenom cesta přímá, protože ani při úpravě svých programů vět-šinou nepůjdete přímo. Při úpravách programů se často stává, , že jed-nou něco upravíme a za chvíli tuto úpravu zrušíme a nahradíme jiúpravou jinou. Říkám vám to dopředu, abyste pak nebyli překvapeni. Řada začínajících programátorů mívá komplexy z toho, kolik má ve

svých programech chyb. Druhá, neméně početná skupina je zase pře-svědčena, že cokoliv napíše, je nutně bez chyby.

Největší a nejčastější začátečnickou chybou je to, když programá-tor začne hledat chybu místo ve svém programu v překladači, operač-ním systému nebo zlomyslných kolezích.

Tato podkapitola bude možná některým z vás připadat dlouhá.Chtěl bych vám v ní však přiblížit, jak většinou probíhá typický proces ladění programu a pokusit se vás vystříhat výše zmíněné nejčastější za-čátečnické chyby.

Třída musí jít přeložit Nemá smysl dlouze bádat nad tím, co se má a nemá upravit. Nejlepšími rádci jsou vždy překladač a správný testovací program. Začneme proto tím, že požádáme o překlad našeho kódu.

Při první žádosti o překlad se překladač zastaví na řádku s hlavičkou třídy a v dolním informačním poli oznamuje:

Strom is not abstract and does not override abstract method nakresli(Java.awt.Grapnics2D) in IKreslený

Zrovna k této chybě sice nápověda neexistuje, ale z toho, co jsme si před chvílí ří-kali asi sami odhadnete, že se překladači nelíbí, že třída Strom sice ve své hlavičce deklarovala, že implementuje rozhraní IKreslený, avšak neimplementovala jeho metodu. Pojďme to napravit – upravíme stávající metodu nakresli tak, aby vyho-vovala požadavkům rozhraní a tím i aktivního plátna.

Změna naštěstí nebude příliš složitá a neměla by nás ani překvapit, protože jsme ji vlastně naplánovali. Stávající metodě proto přidáme požadovaný parametr (kreslítko) a ten předáme metodám, které nakreslí korunu a strom (bez kreslítka by se nyní již nakreslit neumějí).

Kromě toho tuto metodu ve zdrojovém souboru přeřadíme do sekce, kam budeme dávat metody vyžadované implementovanými rozhraními. Výsledný prográmek tedy vypadá následovně:

Page 254: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

254 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 254 z 433

1 2 3 1 4 5 6 7 8

10 11 12

//== PŘEKRYTÉ METODY IMPLEMENTOVANÝCH ROZHRANÍ ================================= /*************************************************************************** * Vykreslí obraz své instance na plátno. * * @param kreslítko Objekt, jehož prostřednictvím se má instance nakreslit. */ public void nakresli(java.awt.Graphics2D kreslítko) { koruna.nakresli(kreslítko); kmen .nakresli(kreslítko); }

Takže pokračujeme dál a požádáme o další překlad. Tentokrát nám překladač za-staví na řádku Plátno.getPlátno().setRozměr( šířka, výška );

kde nám oznámí:

cannot resolve symbol – variable Plátno

Nápověda by vám prozradila, že se používáte proměnnou, která není deklarova-ná, ale při pohledu na označený řádek vám bude hned jasné, že chyba je jinde: v programu používáme třídu Plátno, která v daném projektu vůbec není definová-na. Místo ní tu přece máme AktviníPlátno.

Dopředu víme, že tato chyba se bude v souboru vyskytovat vícekrát. Potře-bovali bychom projít celý soubor a všechny výskyty obyčejného plátna nahradit aktivním plátnem. BlueJ nám k tomu naštěstí nabízí prostředky.

Přesuňte se na začátek souboru a stiskněte v okně editoru na liště s tlačítky tlačítko Najít… Otevře se dialogové okno, které vyplňte podle obr. 6.7, tj. zadejte vyhledávání slova Plátno, jeho nahrazení slovem AktivníPlátno s respektováním velkých písmen a vyhledáváním pouze celých slov. Stiskem tlačítka NajraditVše po-žádejte o hromadné nahrazení tohoto slova.

Obrázek 6.7 Dialogové okno pro vyhledání a nahrazení textu

Editor provede, co jsme mu přikázali a v informačním poli vypíše:

Page 255: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 255

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 255 z 433

Replaced 4 instances of Plátno

z čehož pochopíme, že našel a nahradil 4 výskyty slova Plátno.

Jak jste si mohli všimnout v dialogovém okně Najít, editor umí hledat pouze nahoru a dolů. Proto jsme se na před vyvoláním tohoto oknapřesouvali na začátek souboru. Druhou možností je nikam se nepře-souvat, vyhledat a nahradit vše směrem dolů, změnit hodnotu přepína-če na Hledat nahoru a vyhledat a nahradit vše ještě směrem nahoru.

Požádáme znovu o překlad. Tentokrát se překladač zastaví na příkazu koruna.nakresli()

a v informačním poli nám oznámí:

nakresli(Java.awt.Graphics2D) in Elipsa cannot be applied to ()

Nápověda vám prozradí, že používáte takovou sadu parametrů, pro kterou není definována přetížená verze metody. Podíváte-li se ale na označený příkaz, je to zřejmé: před chvílí jsem přece říkal, že nyní už se nikdo neumí nakreslit bez toho, že by obdržel kreslítko. Elipsa proto metodu nakresli() vůbec nedefinuje.

Tak babo raď – co s tím? Musíme na to úplně jinak. Na počátku kapitoly jsme si říkali, že nyní se už nikdo nebude kreslit sám od sebe, ale vždy pouze na výzvu aktivního plátna, které mu k tomu předá příslušné kreslítko. Tím aktivní plátno zařídí, že nikdo nezmění obraz na plátně, aniž by se o tom dozvědělo – jinak by totiž za něj nemohlo ručit

Podíváte-li se do dokumentace aktivního plátna, zjistíte, že tyto věci řeší tak, že každý, kdo se rozhodne, že by měl vypadat jinak, než je právě zobrazován, po-žádá aktivní plátno o to, aby se znovu překreslilo. Když je pak tento objekt v rám-ci překreslování požádán, aby se nakreslil, nakreslí se v nové podobě.

Z toho vyplývá i řešení našeho problému: kdykoliv se změní vzhled nebo po-zice objektu, musí požádat aktivní plátno, ať se překreslí. Metodu setPozice(int, int), ve které k chybě došlo, bychom tak mohli přepsat do tvaru: 1 2 3 1 4 5 6

public void setPozice(int x, int y) { koruna.setPozice( x, y ); kmen .setPozice( x + (koruna.getŠířka() - kmen.getŠířka()) / 2, y + koruna.getVýška() ); AktivníPlátno.getPlátno().překresli(); }

Při dalším pokusu o překlad se překladač zarazí na příkazu koruna.smaž();

Page 256: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

256 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 256 z 433

o němž tvrdí:

cannost resolve symbol – method smaž()

Nové základní obrazce kreslené na aktivní plátno totiž nemají metody nakresli() a smaž(), aby nedocházelo k záměně nové a staré metody nakresli(). Pokud by-chom chtěli náš strom z plátna opravdu smazat, museli bychom to stejně dělat ji-nak: museli bychom požádat plátno, aby jej odstranilo ze seznamu těch, které spravuje. Metoda by pak získala tvar: 1 2 3 4 5 6 7

/*************************************************************************** * Odstraní obraz své instance z plátna. */ public void smaž() { AktivníPlátno.getPlátno().odstraň( this ); }

Další pokus o překlad kódu skončí opět chybou. Překladač se opět zarazí ne pří-kazu, v němž po koruně chceme, aby se nakreslila. Tentokrát je to v metodě posunDolů(int): 1 2 3 4 5 6

public void posunDolů( int vzdálenost ) { koruna.posunDolů( vzdálenost ); kmen .posunDolů( vzdálenost ); koruna.nakresli(); }

Tuto chybu bychom mohli opravit stejně, jako jsme ji upravovali v metodě setPozice(int,int). Jenže když trochu zavzpomínáte, tak si možná vybavíte, že jsme zde korunu kreslili proto, že nám při některých přesunech její část odmazá-val kmen. Když ale teď aktivní plátno ručí za to, že vše bude nakresleno správně, můžeme vše opravit jednoduše tak, že tento příkaz prostě odmažeme.

Další pokus o překlad již prochází – první etapu úpravy našeho zdrojového kódu máme tedy za sebou.

Testování Třídu jsme upravili, jdeme tedy upravovat testy. Mohli bychom sice vytvořit no-vé, ale když jsme do přípravy stávajících testů vložili tolik námahy, tak většinou chceme, aby se nám vyplatila. Pokusíme se tedy zprovoznit staré testy v novém projektu.

Page 257: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 257

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 257 z 433

Načtěte proto z minulého projektu třídu StromTest a otevřete její zdrojový text. Zkusíme postupovat stejně jako v případ třídy Strom – pokusíme se třídu v novém projektu přeložit.

Na počátku se nám překladač opět vzbouří, že nezná třídu Plátno. Tuto chy-bu ale již známe a víme jak ji řešit: odkaz na třídu Plátno nahradíme odkazem na AktivníPlátno.

Při dalším překladu se překladači nebude líbit, že instance třídy Strom nemají definovanou metodu nakresli(). Tady se musíme zamyslet – máme totiž dvě možnosti:

Místo abychom strom kreslili, požádáme aktivní plátno, aby příslušnou in-stanci přidala do svého seznamu.

Řekneme si, že když může mít instance definovanou metodu smaž(), tak by-chom jí mohli definovat i metodu nakresli(), která by měla za úkol přihlásit svoji instanci u aktivního plátna.

Mně se líbí víc druhá možnost, protože naznačuje, že by nám mohla občas ušetřit nějaké to psaní. Otevřeme proto znovu zdrojový kód třídy Strom a přidáme do něj metodu nakresli(): 1 2 3 4 5 6 7

/*************************************************************************** * Přihlási instanci u aktivního plátna do jeho správy. */ public void nakresli() { AktivníPlátno.getPlátno().přidej( this ); }

Přeložíme třídu Strom a po úspěšném překladu se znovu pokusíme přeložit třídu StromTest. A vida – tentokrát ji překladač bez protestů celou přeloží.

Jdeme tedy testovat. Začneme s prvním testem – NakresliSmaž. A hned vý-buch – test neprošel a okno Výsledky testů nám oznamuje, že jsme špatně zadali ně-jaké rozměry. Podíváme-li se podrobně do výpisu chyb ve spodní polovině okna, zjistíme, že k chybě mělo dojít na 48. řádku třídy StromTest v metodě setUp().

Na tomto řádku se pouze nastavuje rozměr aktivního plátna – věc, kterou jsme s obyčejným plátnem dělali mnohokrát. Nejlepší bude zaběhnout do doku-mentace a podívat se, co můžeme dělat špatně.

A jsme doma! V dokumentaci si totiž můžeme přečíst, že metoda setRozměr(int,int) nastavuje políčkovou velikost plátna – my jsme po plátnu chtěli, aby bylo 300 polí široké a 300 vysoké. Změníme proto nastavovanou veli-kost plátna na 6×6 polí a zkusíme naše testy znovu.

Page 258: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

258 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 258 z 433

Obrázek 6.8 Z posledního řádku vyčteme, že k chybě došlo v metodě setUp třídy Strom na řádku 48

Test tentokrát bez problému prošel. Zkusíme tedy další v řadě, kterým je test po-sunů. Tady opět zakopáváme: dialogové okno sice vyhrožuje, že bude následovat posun, ale stromy se ani nehnou.

Zkusíme, jestli nám testy navzájem neinterferují (tj. jestli první po sobě neza-nechal něco, co by druhému bránilo v činnosti). Zresetujeme virtuální stroj a spus-tíme test posunů znovu. Ještě horší – tentokrát se stromy ani neobjevily.

Otevřeme proto zdrojový kód testovací třídy a podíváme se, co může být špatně. Po chvilce přemýšlení byste na to jistě přišli: my jsme sice v této metodě vytvořili instance stromů, ale žádnou z nich jsme nepřihlásili do správy plátna. Jak víme, aktivní plátno zobrazuje pouze ty objekty, které se přihlásí do jeho správy.

Náprava je jednoduchá: do metody setUp() zavoláme pro každý vytvořený strom jeho metodu nakresli() (abychom to měli úplně jednoduché, můžeme po-třebnou čtveřici příkazů zkopírovat z metody testNakresliSmaž()) a zkusíme test spustit znovu. Tentokrát proběhlo vše bez chyby.

Následující test nastavení kroku proběhne sice bez chyby, ale hned další test Zarámuj opět skončí „v červených“ a opět pro špatně zadané rozměry. Jak si zjis-tíme v okně Výsledky testů, chyba je nyní nejspíše v metodě zarámuj().

Podíváme se do ní a je nám to jasné. Je tu stejná chyba jako na počátku – opět nastavujeme bodovou velikost plátno místo políčkové. Tady ale nemůžeme říct, kolik polí má být plátno velké, protože velikost našeho stromu nemusí být násob-kem velikosti pole.

Nahlédneme proto do dokumentace, jestli bychom tam nenašli něco, co by nám mohlo pomoci. A vskutku – pro tento účel by se nám krásně hodila metoda

Page 259: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 259

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 259 z 433

setKrokRozměr(int,int,int), kterou můžeme současně nastavit i velikost kroku a tím i velikost jednoho pole. Zadáme proto velikost kroku rovnu jedné a rozměry pak mohou zůstat takové, jaké jsme je nastavovali dříve.

Spustíme test. První strom nám metoda zarámuje dobře, ale druhý stroj se začne přetahovat o své místo na slunci s kolegy (viz obr. 6.9).

Obrázek 6.9 Při pokusu o zarámování druhého stromu nám na obraze překážejí jeho kolegové

Za vše může aktivní plátno, protože se neustále stará o to, aby vše, co přihlásíme a nesmažeme, bylo zobrazeno. Musíme proto upravit metodu zarámuj tak, že nej-prve požádá plátno, aby všechny ostatní obrazce z plátna odstranilo a pak přihlásí svoji instanci, kterou nechá zobrazit a zarámovat. Výsledná podoba metody za-rámuj tedy bude vypadat následovně: 1 2 3 4 5 6 7 8 9

10 11

/*************************************************************************** * Odstraní z plátna všechny ostatní instance a nastaví * parametry okna s plátnem tak, aby právě zarámovalo danou instanci. */ public void zarámuj() { AktivníPlátno.getPlátno().odstraňVše(); AktivníPlátno.getPlátno().setKrokRozměr( 1, getŠířka(), getVýška() ); setPozice( 0, 0 ); AktivníPlátno.getPlátno().přidej( this ); }

Tak znovu přeložíme a vyzkoušíme. Funguje! Paráda, můžeme pokračovat dalším testem.

Spustíme test PočítáníInstancí. Jenže ouvej – plátno vypadá po spuštění tes-tu nějak degenerovaně (viz obr. 6.10)! Protože tento test pouze vypisuje textovou podobu instancí, bude chyba asi opět v přípravku, tj. v metodě setUp().

Page 260: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

260 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 260 z 433

Obrázek 6.10 Plátno v průběhu testů zdegenerovalo

Při pohledu na její zdrojový text je vše jasné. Metoda nastavuje velikost plátna 6×6 polí, jenže metoda zarámuj prověřovaná v minulém testu zadala velikost kroku rovnu 1. Přípravek tedy správně generoval plátno velikosti 6×6 bodů. Musíme změnit metodu nastavující počáteční velikost plátna a zadat nejenom jeho políč-kový rozměr, ale také velikost políčka.

Spustíme nový test a opět v pořadí Zarámuj – PočítáníInstancí. Okno při něm má již správnou velikost, ale na plátně zbyl ještě strom od minula. Zapomněli jsme, že před přihlášením stromů přípravku musím plátno nejprve vyčistit.

Navíc se již na plátně neobjevují čáry, které ohraničovaly jednotlivá pole a usnadňovaly nám tak kontrolu velikosti a pozice jednotlivých obrazců. Je to pro-to, že při nastavení velikosti kroku rovné jedné se zároveň vypne zobrazování těchto čar. Chceme-li je vidět, musíme je znovu zapnout zavoláním metody setMřížka(boolean).

Znovu upravíme metodu setUp() a všechny objevené chyby napravíme. Po této úpravě bude vypadat následovně: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15

protected void setUp() { AktivníPlátno plátno = AktivníPlátno.getPlátno(); plátno.setKrokRozměr( 50, 6, 6 ); plátno.setMřížka( true ); plátno.odstraňVše(); strom1 = new Strom( 0, 0, 100, 150, 10, 3 ); strom2 = new Strom( 0, 150, 100, 150, 5, 4 ); strom3 = new Strom( 100, 100, 200, 200, 20, 2 ); strom4 = new Strom( 100, 0, 150, 100, 3, 5 ); strom1.nakresli(); strom2.nakresli(); strom3.nakresli(); strom4.nakresli(); }

Nyní se již počítání instancí rozběhlo. Zbývá poslední test, kterým je vygenerová-ní zarámovaného obrázku ve středu plátna. A jako obyčejně, test opět nechodí – žádný zarámovaný strom nikde není.

Předpokládám, že již tušíte, kde hledat chybu: rám i strom se zapomněly při-hlásit u animačního plátna. Opravíme proto metodu obrázek(Oblast,int) – její nová podoba může být např. následující:

Page 261: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 261

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 261 z 433

1 2 3 4 5 6 7 8 9

10 11

public static void obrázek( Oblast oblast, int šířkaRámu ) { AktivníPlátno.getPlátno().přidej( new Obdélník(oblast, Barva.ČERNÁ) ); oblast.x += šířkaRámu; oblast.y += šířkaRámu; oblast.šířka -= 2*šířkaRámu; oblast.výška -= 2*šířkaRámu; AktivníPlátno.getPlátno().přidej( new Obdélník(oblast, Barva.AZUROVÁ) ); new Strom( oblast ).nakresli(); }

Všechny metody instancí jsme se snažili zahrnout do našich testů. Vy-tvoření testů však odolaly statické metody zarámuj(int,int) a alej(). Když se je pokusíte spustit, zjistíte, že jsou v nich samozřejmě opětznámé chyby. Jejich odhalení, odstranění a doplnění potřebných testů (abychom příště nemuseli myslet na to, že pro tyto metody nemají svoje testy) vám dám za domácí úkol.

Podobu tříd Strom a StromTest ve stavu, do kterého jsme je právě do-provodili (včetně domácího úkolu) najdete v projektu 06_Rozhraní_Z ve třídách Strom_6a a Strom_6aTest.

Závěrečné úpravy Třídu i její testy jsme tedy rozchodili. Nyní bychom ještě měli proběhnout její zdrojový kód a podívat se, jestli bychom něco nemohli udělat lépe. V následují-cích odstavcích budu většinou pouze naznačovat, co by se dalo vylepšit nebo upravit. Vlastí realizaci nechám většinou na vás.

Uložení odkazu na Plátno do atributu třídy První věc, které bychom si mohli všimnout, je skutečnost, že ve třídě často pracu-jeme a aktivním plátnem, které vždy získáváme pomocí metody getPlátno(). Toto plátno se ale nemůže změnit – víme přece, že je jedináček.

Mohli bychom proto kód vylepšit tak, že bychom si zavedli statickou kon-stantu (pojmenujme ji třeba AP), do ní uložili odkaz na plátno a v celém kódu pak sekvenci AktivníPlátno.getPlátno() nahradili odkazem AP.

Obdobnou změnu bychom mohli udělat i v testovací třídě – přidali bychom do přípravku proměnnou AP a uložili do ní odkaz na aktivní plátno.

Page 262: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

262 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 262 z 433

Odstranění statického atributu krok Třídy základních obrazců již v tomto projektu nemají statický atribut krok, proto-že odvozují délku posunu bezparametrických posunových metod od velikosti po-le aktivního plátna, tj. od hodnoty AP.getKrok(). Změnou velikosti kroku plátna se tak mění velikost kroku všech obrazců a všechny obrazce se při použití bezpara-metrických posunových metod posunou o stejnou vzdálenost. Mohli bychom to zavést i v naší třídě Strom.

Při odstranění definice atributu krok a příslušných přístupových metod ne-smíme samozřejmě zapomenout na odpovídající změnu definice testovací třídy. V té budeme muset použít místo atributu krok volání AP.getKrok().

Úpravy posunových metod V naší třídě definujeme způsob, jakým se instance přesouvá, na třech různých místech: v metodě setPozice(int,int), posunVpravo(int) a posunDolů(int).

Již několikrát jsem říkal, že není dobré definovat stejnou činnost na několika místech. Pokud bychom se později rozhodli, že náš strom vylepšíme (např. ne-cháme v koruně nakreslit pár zralých jablek), museli bychom algoritmus posunu opravit na třech místech.

Z hlediska snazší spravovatelnosti kódu by proto bylo lepší, kdybychom chování metod pro posun vpravo a dolů odvodili od metody setPozice(int,int). Metodu posunVpravo(int) bychom tak upravili do tvaru: 1 2 3 4

public void posunVpravo( int vzdálenost ) { setPozice( getX()+vzdálenost, getY() ); }

Zefektivnění přesunu V dokumentaci třídy AktivníPlátno si můžeme přečíst:

Page 263: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 263

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 263 z 433

Efektivita vykreslování Efektivitu vykreslování je možné ovlivnit voláním metody nekresli(), která pozastaví překreslování plátna po nahlášených změnách. Její volání je vý-hodné např. v situaci, kdy je třeba vykreslit obrazec složený z řady menšíchobrazců a bylo by nevhodné překreslovat plátno po vykreslení každéhoz nich.

V takovém případě totiž nejde jenom o to, že se plátno zbytečně mockrát překresluje, ale také o to, že při takovémto překreslování obrázek často nepří-jemně bliká, protože se na plátně objevují části obrazců, která vzápětí jiný ob-razec překryje. Proto je výhodné v průběhu tvorby obrazce překreslování pozastavit a nechat plátno překreslit až v okamžiku, kdy je nový vzhled ob-razce definován.

Do původního, tj. kreslícího stavu převedeme plátno voláním metodyvraťKresli(), která vrátí vykreslování do stavu před posledním voláním me-tody nekresli(). Nemůžeme totiž plátnu nařídit, aby se hned začalo kreslit,protože nevíme, jestli vykreslován našeho obrazce není součástí nějakéhovětšího vkreslování, pro které již bylo překreslování plátna pozastaveno.Kdybychom překreslování nyní zapnuli, zapnuli bychom je možná uprostřed onoho většího překreslování.

Proto plátno pouze žádáme, aby se vrátilo do toho kreslícího stavu, vekterém bylo v okamžiku, kdy jsme je naposledy žádali o to, aby se přestalopřekreslovat. Nemůže se tedy stát, že by se při zavolání metody nekresli() v situaci, kdy je již vykreslování pozastaveno, začalo po následném zavolánívraťKresli() hned vykreslovat. Po dvou voláních nekresli() se začne vy-kreslovat až po dvou zavoláních vraťKresli().

Chceme-li proto zvýšit efektnost přesunových operací a zamezit tomu, aby bylo na pomalejších počítačích poznat, že se u stromu přesouvá nejprve koruna a te-prve pak kmen, měli bychom upravit metodu setPozice() tak, aby nejprve zaká-zala překreslování, pak změnila nastavení koruny a kmene a pak opět překreslování zapnula (teď se nám hodí, že jsme algoritmu pro veškeré přesuny koncentrovali do jediné metody, kterou ostatní volají). Po úpravě by metoda moh-la vypadat následovně: public void setPozice(int x, int y) { AP.nekresli(); koruna.setPozice( x, y ); kmen .setPozice( x + (koruna.getŠířka() - kmen.getŠířka()) / 2, y + koruna.getVýška() );

Page 264: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

264 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 264 z 433

AP.vraťKresli(); }

V předchozí metodě jsem chtěl zdůraznit, že se dvojice metod ovlivňu-jících vykreslování chová jako závorky a proto jsem příkazy mezi nimiodsadil. To, jestli budete používat podobné zvýraznění i ve svých pro-gramech, ponechám na vašem vkusu.

Podobu tříd Strom a StromTest ve stavu, do kterého jsme je právě do-provodili najdete opět v projektu 06_Rozhraní_Z ve třídách Strom_6b a Strom_6bTest.

Zkuste obdobným způsobem přizpůsobit novým podmínkám defino-vaným v projektu 06_Rozhraní_A i vaší vlastní třídu.

6.7 Implementace několika rozhraní Třída může implementovat libovolný počet rozhraní současně. Deklarují-li přitom dvě rozhraní metodu se stejnou hlavičkou, stačí, když třída danou metodu im-plementuje jen jednou. No, stačí je špatné slovo – ono to totiž ani jinak nejde.

Implementaci druhého rozhraní si nebudeme ukazovat na nějakém konkrét-ním projektu (na ten dojde za chvíli), ale předvedeme si ji na obyčejném pomoc-ném rozhraní, které nazveme ISmazat (to abychom věděli, co pak s ním). Toto rozhraní bude deklarováno následovně: 1 2 3 4 5

public interface ISmazat { void nakresli( java.awt.Graphics2D kreslítko ) void smazat() }//public interface ISmazat

Otevřete nyní zdrojový kód třídy Strom tak, abyste viděli na její hlavičku a natáh-něte od ní implementační šipku k rozhraní ISmazat. Hlavička nyní dostala tvar

public class Strom implements IKreslený, ISmazat

Page 265: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 265

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 265 z 433

Pokusme se nyní třídu Strom přeložit. Překladač se vzbouří a vypíše známé hláše-ní

Strom is not abstract and does not override abstract method smazat() in ISmazat

Definujte proto potřebnou metodu s prázdným tělem (stejně ji budeme za chvíli mazat, takže ji můžete pro tu chvíli napsat hned za hlavičku):

public void smazat() {}

Nyní by již měl překlad proběhnout bez námitek (leda byste někde udělali něja-kou jinou chybu).

Zkusme nyní přidat do projektu další rozhraní – např. ISmazat2. Abychom měli život jednodušší, už v něm žádnou metodu deklarovat nebudeme a pouze k němu od stromu natáhneme implementační šipku.

V hlavičce třídy Strom přibude další položka v seznamu implementovaných rozhraní, ale jinak se nic nezmění. Rozhraní po třídě nechce nic, co by neuměla, takže její překlad proběhne opět bez problémů. Stejně by to dopadlo i tehdy, po-kud by rozhraní deklarovalo některou z metod, které Strom již dávno implemen-tuje.

Vrátím-li se opět k naší analogii, tak bychom mohli říci, že třídě nic ne-brání v tom, zařídit svým instancím několik licencí na různé činnosti. Bude-li nějaká licence vyžadovat např. přítomnost robota-metody horo-lezce, který bude umět vylézt do zadaného patra, pak není důvod, pročbyl jeden a týž robot nemohl být použit při všech obdobných horole-zeckých pracech nezávisle na tom, pod jakou licencí je aktuální činnost právě provozována.

Odvolání implementace rozhraní Někdy se stane, že si v průběhu doby svůj původní záměr rozmyslíme a rozhod-neme se implementaci rozhraní třídou odvolat.

To, že k tomu stačí smazat příslušnou klauzuli v hlavičce je vám nejspíš jasné. Zkuste např. smazat informaci o tom, že Strom implementuje ISmazat a uvidíte, jak po překladu třídy zmizí i příslušná implementační šipka.

Implementační šipky ale můžete zrušit i přímo v diagramu tříd aniž byste kvůli tomu museli otevírat zdrojový kód třídy. Stačí na šipku klepnout pravým tlačítkem myši. Tím šipka „ztloustne“ (to abyste věděli, kterou jste vybrali) a zá-roveň rozbalíte místní nabídku s jediným příkazem: Odstranit (viz obr. 6.11). Zadej-te jej a šipka zmizí. Současně zmizí i implementační klauzule z hlavičky třídy.

Page 266: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

266 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 266 z 433

Obrázek 6.11 Zrušení implementace v diagramu tříd

Před dalším čtením odstraňte z definice třídy Strom definici metody smazat() a současně odstraňte i obě pomocná „mazací“ rozhraní.

6.8 Návrhový vzor Služebník (Servant) Naše dosavadní tvorba dalších tvarů nebyla příliš efektivní. Když jsme navrhli ně-jaký nový tvar (jak strom, o kterém si pořád vyprávíme, tak vámi definované tva-ry), tak jsme v příslušné třídě definovali také všechny metody pro manipulaci s tímto objektem. Přitom tyto metody byly pro většinu objektů téměř stejné – často stačilo vzít jejich definice z jiné třídy a do právě definované třídy je zkopírovat.

Mezi důležité programátorské zásady patří vyvarovat se kopírování kódu. Kopie kódu totiž přinášejí obdobné problémy jako magické hodnoty (literály), o nichž jsem hovořil v kapitole Konstanty a literály na straně 143. Když totiž později zjistíte, že jste něco naprogramovali špatně a nebo se změní zadání a je potřeba

Page 267: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 267

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 267 z 433

kód změnit, musíte projít všechna místa s tímto kódem a všude zanést stejnou opravu, což je potenciální zdroj nepříjemných chyb.

Opakování částí kódu se dá zamezit různými metodami, s nimiž vás budu postupně seznamovat. Nyní si ukážeme jednu z nich, kterou je definice služební-ka. Služebník je třída, jejíž instance (případně i ona sama) poskytují metody, kte-ré si vezmou potřebnou činnost (službu) na starost, přičemž objekty, s nimiž (nebo pro něž) danou činnost vykonávají, přebírají jako parametry.

Návrhový vzor služebník je další ze vzorů, které jsou tak primitivní, že se do knihy GoF nedostaly. Jak jsem již říkal, začátečnické učebnice o návrhových vzorech nehovoří a pro pokročilé programátory je tatokonstrukce příliš primitivní na to, aby jí dávali jméno. Chtěl-li jsem se na tento vzor v učebnici dále odvolávat, musel jsem mu jméno vymys-let sám.

Proč rozhraní Toho, aby byla jedna metoda schopna zpracovávat parametry různých typů, mů-žeme dosáhnout dvěma způsoby:

definicí několika přetížených verzí dané metody,

definicí rozhraní, které deklaruje vlastnosti objektu potřebné pro to, aby me-toda mohla splnit svůj úkol.

První řešení, tj. definici sady přetížených metod, můžeme použít pouze tehdy, víme-li dopředu, že počet typů parametrů, které má metoda zpracovávat, je do-předu známý a relativně malý.

Druhé řešení využívající definice rozhraní je mnohem univerzálnější, protože nijak dopředu neomezuje počet datových typů, pro jejichž instance bude možné danou metodu použít. Kdykoliv budeme v budoucnu potřebovat zahrnout mezi „obhospodařované“ datové typy další třídu, stačí, aby implementovala příslušné rozhraní a její instance mohou začít vystupovat jako parametry příslušných me-tod.

Výhodnosti druhého řešení napomáhá i to, že třída může implementovat li-bovolný počet rozhraní současně, takže nic nebrání tomu, abychom pro různé účely definovali různé služebníky a třída pak implementovala rozhraní všech slu-žebníků, jejichž služby chce využívat.

Page 268: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

268 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 268 z 433

S tímto návrhovým vzorem jsme se již seznámil v praxi – naše třída AktivníPlátno není nic jiného, než služebník vyžadující, aby objekty,které chtějí využívat jeho služeb, implementovaly rozhraní IKreslený.Teď si představte, že by pro zařazování nových objektů mezi spravova-né byly použity přetížené metody. To bychom pak byli už jednou pro vždy odkázáni na kreslení trojúhelníků, obdélníků a elips. Tak, jakojsme nyní přidali mezi kreslitelné objekty i čáry a texty, můžeme v bu-doucnu přidat i řadu dalších tvarů – stačí, když budou implementovat rozhraní IKreslený.

Implementace Má-li metoda pracovat s nějakým objektem, většinou potřebuje, aby daný objekt něco uměl (např. aktivní plátno požaduje po objektech, které se hlásí do jeho správy, aby se uměly nakreslit dodaným kreslítkem).

1. Prvním krokem při návrhu služebníka je analýza toho, co má mít na starosti. Musíme si ujasnit, jaké metody musí služebník definovat a co budou tyto me-tody potřebovat od obsluhovaného parametru. Jinými slovy: co bude muset obsluhovaný parametry umět, aby metody mohly bezpečně splnit svůj úkol.

2. Druhým krokem je definice rozhraní, které deklaruje požadované vlastnosti obsluhovaného parametru, tj. vyjmenovává, na jaké zprávy bude muset umět reagovat (jinými slovy: jaké metody musí implementovat). Bude-li chtít něja-ká instance využít služeb metod služebníka, bude muset implementovat toto rozhraní.

3. Třetím krokem je definice testů, které prověří, jestli následně definované me-tody služebníka dělají opravdu to, co mají.

4. Čtvrtým krokem je definice příslušného služebníka (a pokud možno jeho otestování).

5. Pátým krokem je implementace definovaného rozhraní obsluhovanými tří-dami, tj. třídami, s jejichž instancemi mají metody služebníka a/nebo jeho in-stancí pracovat (a opět jejich otestování).

Page 269: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 269

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 269 z 433

Aplikace na náš projekt Ukážeme si, jak lze zařídit, aby se naše grafické objekty mohly plynule pohybovat po plátně aniž bychom museli v jejich třídách definovat metody, které tento po-hyb realizují.

Představte si, že bychom měli k dispozici třídu Přesouvač (ta je tu jako slu-žebník), které bychom vždy předali objekt a ona by jej plynule přesunula do po-žadované cílové pozice. Aby to mohla učinit, museli bychom jí ale předat objekt, který je takového přesunu schopen.

Požadavek na přemístění objektu můžeme obecně zadat dvěma způsoby; zá-leží na tom, zda jej potřebujeme přesunout na zadané souřadnice, nebo zda jej po-třebujeme odsunout o požadovanou vzdálenost. V podstatě však jde o stejný problém, protože kdybychom uměli zjistit současné souřadnice objektu, mohli by-chom jednu metodu realizovat pomocí druhé:

posun o zadanou vzdálenost můžeme řešit tak, že ze současných souřadnic a požadovaného posunu spočteme nové souřadnice a na ty objekt přemístíme,

posun na zadané souřadnice můžeme realizovat tak, že spočteme vzdálenost současných a cílových souřadnic a o tu pak objekt posuneme.

Z předchozích úvah tedy vyplývá, že k tomu, abychom mohli objekt přesouvat, potřebujeme, aby měl implementované metody pro zjištění a nastavení pozice. Definujeme proto rozhraní IPosuvný s následujícími metodami:

getX(), která vrátí aktuální x-ovou souřadnici objektu,

getY(), která vrátí aktuální y-ovou souřadnici objektu,

setPozice(int,int), která přesune objekt na zadanou pozici.

Jeho definice (bez komentářů) by mohla vypadat např. následovně: 1 2 3 4 5 6

public interface IPosuvný { public int getX(); public int getY(); public void setPozice(int x, int y); }//public interface IPosuvný

Zkopírujte si z projektu 06_Rozhraní_Z zdrojové kódy tříd Přesouvač a IPosuvný.

Page 270: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

270 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 270 z 433

Šťouralům dopředu přiznávám, že se třída Přesouvač nechová zcela ko-rektně, protože u parametrů svých metod skrytě předpokládá, že im-plementují rozhraní IKreslený a nejsou-li zobrazeny na plátně, tak je na ně nechá zobrazit. V příští kapitole si ukážeme, jak tuto funkčnost řešitkorektnějšími metodami.

Implementace tohoto rozhraní našimi třídami není problém, protože všechny v úvahu přicházející třídy v projektu (včetně našeho stromu a vašeho paralelně vy-tvářeného obrazce) potřebné metody implementují. Nemusíme proto nic dopro-gramovávat, stačí pouze natáhnout implementační šipky od každé „posuvné“ třídy k implementovanému rozhraní (viz obr. 6.12).

Podíváte-li se nyní na hlavičku kterékoliv z tříd, od nichž jste natáhli druhou implementační šipku k rozhraní IPosuvný, uvidíte, že hlavička přihlašuje třídu k implementaci dvou rozhraní – např. hlavička třídy Strom bude mít tvar:

public class Strom implements IKreslený, IPosuvný

Závěrečný test Nyní už stačí pouze připravit text, abychom ověřili, že všechno funguje jak má. Postupujte následovně:

1. Nejsou-li ještě všechny třídy přeloženy, přeložte projekt.

2. V místní nabídce třídy StromTest zadejte Vytvořit testovací metodu.

3. Pomocí bezparametrického konstruktoru vytvořte instanci třídy Přesouvač a nazvěte je přes1.

Page 271: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 271

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 271 z 433

Obrázek 6.12 Projekt po přidání třídy Přesouvač a rozhraní IPosuvný

a po natažení příslušných implementačních šipek

4. Pošlete tomuto přesouvači zprávu přesunO(objekt,doprava,dolů), kde jako objekt zadáte instanci strom3 a zbylé dva parametry nastavíte na -100. Pře-souvač přesune strom z pravého dolního rohu do levého horního rohu.

5. Pomocí konstruktoru s celočíselným parametrem vytvořte přesouvač s rych-lostí 5 a nazvěte jej přes5.

6. Pošlete tomuto přesouvači zprávu přesunO(objekt,doprava,dolů), kde jako objekt zadáte instanci strom1 a necháte jej přesunout o 200 bodů vpravo a 0 bodů dolů.

7. Přesouvači přes5 pošlete ještě zprávu přesunNa(objekt,x,y), kde jako objekt zadáte instanci strom4, kterou necháte přesunout na souřadnice x=200 a y=150.

8. Ukončete generování testu.

Page 272: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

272 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 272 z 433

Definujte třídu Pšouk s dvěma konstruktory: bezparametrickým a jed-noparametrickým s celočíselným parametrem síla. Vyvolání bezpara-metrického konstruktoru bude mít stejný efekt, jako vyvoláníjednoparametrického konstruktoru s parametrem rovným jedné.

Ve třídě definujte metodu přifoukni(IPosuvný), která objekt předaný jako parametr třikrát zvětší o počet bodů rovný síle dané instance a me-todu ufoukni(IPosuvný), která svůj parametr stejným způsobem zmen-ší.

Aby jednotlivé kroky zvětšení nebo zmenšení nenásledovaly mocrychle za sebou, zavolejte mezi nimi metodu P.čekej(int), které zadáte počet milisekund, které má počkat před tím, než program pustí dál.

Vymyslete, jak musí být definováno rozhraní INafukovací, a by in-stance třídy Pšouk mohly svůj úkol splnit, a definujte je.

Definujte třídu PšoukTest, která prověří funkčnost vámi definované třídy.

Tentokrát vám již vzorové řešení ukazovat nebudu, protože se domní-vám, že už tak velkou berličku nepotřebujete. Najdete je však (podle očekávání) v projektu 06_Rozhraní_Z. Vedle očekávané třídy Pšouk a roz-hraní INafukovací zde najdete také třídu Kompresor, jejíž instance umějí předaný objekt typu INafukovací plynule zvětšit, resp. zmenšit o nebo na libovolnou zadanou velikost.

6.9 Refaktorování V průběhu vývoje programu se velmi často dostaneme do situace, ve které zjistí-me, že program není pro naše účely optimálně navržen. Nejde o to, aby program dělal něco jiného. Stále chceme, aby dělal přesně to, co dělá doposud. Jenom by se nám hodilo, kdybychom měli program navržený trochu jinak.

Ve všech učebnicích návrhu programových systémů se dozvíte, že není moudré začít programovat hned po obdržení zadání, ale že je nanejvýš nutné nej-prve celý problém důkladně zanalyzovat, abychom se pak při kódování (tj. při zápisu navrženého programu) pokud možno vystříhali slepých uliček a nemuseli zbytečně velké části programu předělávat.

Page 273: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 273

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 273 z 433

Zkušenost ukazuje, že dobře provedená analýza výrazně zrychluje a zefek-tivňuje celý projekt, nicméně nezávisle na důkladnosti předběžné analýzy se prakticky vždy objeví situace, kdy je třeba v původním návrhu něco změnit.

Vedle rozsáhlých výzkumů jak zadaný problém co nejlépe zanalyzovat a při-pravit pro následné kódování proto začali počítačoví vědci bádat i nad tím, jak v případě, kdy se přes veškerou předchozí snahu ukáže, že je třeba návrh pro-gramu změnit, provést tuto změnu s co nejmenším úsilím a co nejlepším výsled-ným efektem1.

Změny kódu, jejichž úkolem je zlepšit návrh programu, avšak zachovat veš-kerou jeho stávající funkčnost, jsou označovány jako refaktorování (anglicky refactoring). Pokroky ve výzkumu jeho možností a standardizace refaktorovacích postupů mají dva viditelné důsledky:

Programátoři se přestali bát měnit hotový kód, protože se naučili postupy, jak to dělat rychle, bezpečně a levně.

Řada standardizovaných refaktorovacích postupů se stala součástí základní výbavy profesionálních vývojových prostředí. Tím se refaktorování stalo ještě snadnější, levnější a bezpečnější.

Refaktorace spočívá v postupné aplikaci série drobných změn. Každé z těchto změn musí být tak drobná, aby bylo na první pohled jasné, že se chování systému v důsledku provedené úpravy nezmění. Potřebujeme-li udělat nějakou větší změ-nu, rozložíme ji nejprve na sérii jednoduchých změn a ty pak provedeme jednu po druhé. (Někdy to nejde, ale měli byste se o to vždy pokusit.)

To ale nestačí – po každé změně je totiž potřeba celý kód znovu otestovat. Jsme totiž lidé chybující a i při provádění naprosto přehledných úprav dokážeme udělat chybu. Kdybychom netestovali po každém jednoduchém kroku, po kterém ještě přesně víme, co jsme v systému změnili, bylo by hledání případné chyby pří-liš obtížné.

Ukázka Ukažme si jednu takovou jednodušší situaci na příkladu. V naší třídě Strom jsme doposud definovali metodu setPozice(int, int), ale nedefinovali jsme metodu setRozměr(int,int). Důvodem pro tuto absenci bylo to, že instance třídy Strom se

1 Filozofii refaktorování, jeho principy, metody a nejdůležitější vzory jsou skvěle popsány

v knize Fowler M.: Refactoring: Improving the Design of Existing Code, Addison Wesley, 2002, která vyšla v českém překladu pod názvem Refactoring – Zlepšení existujícího kódu, Grada, 2003, ISBN 80-247-0299-1.

Page 274: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

274 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 274 z 433

konstruovaly s nastaveným poměrem šířky, resp. výšky kmene ku šířce, resp. výšce celého stromu.

Tyto poměry bychom se sice mohli pokusit získat dotazem na aktuální veli-kost koruny a kmene a následným výpočtem, ale vzhledem k zaokrouhlovacím chybám bychom v některých situacích nemuseli obdržet původně zadané hodno-ty1, takže když bychom strom zmenšili a opět zvětšili, mohl by pak vypadat jinak.

Pokusíme se proto definovat metodu setRozměr(int,int) nyní. Při té příleži-tosti si ukážeme, jak lze v podobných případech postupovat. Znovu bych zopa-koval: postupuje se ve velmi malých krocích a po každém kroku je třeba upravení program hned otestovat.

Přiznám se, že jsem si tuto definici schválně chovával až sem, protože díky aktivnímu plátnu se nám nebudou jednotlivé objekty odmazávat a navíc si můžeme na plátně natáhnout síť čar, které nám umožní lépekontrolovat, že nově nastavené rozměry odpovídají našim požadav-kům.

1. krok: Vytvoření testu Jdeme na to. Jak jsme si v pasáži TDD – vývoj řízený testy na straně 104 řekli, TDD vyžaduje, abychom nejprve napsali testy a teprve pak testovaný program.

V prvním kroku bychom měli připravit testy. Ve třídě Strom proto definujeme prázdnou metodu setRozměr(int,int) (tu vám snad ukazovat nemusím) a v tes-tovací třídě StromTest pak metodu testSetRozměr(). Aby byl celý postup opravdu průzračný, definuji testovací metody „ručně“: 1 2 3 4 5 6 7 8 9

10 11 12 13 14

public void testSetRozměr() { ap.setKrokRozměr(10, 30, 30); assertEquals(true, P.souhlas( "Strom 1: x=0, y= 0, š=100, v=150, šSt/šKm=10, vSt/vKo=3\n" + "Strom 2: x=0, y=150, š=100, v=150, šSt/šKm= 5, vSt/vKo=4\n" + "Strom 3: x=100, y=100, š=200, v=200, šSt/šKm=20, vSt/vKo=2\n" + "Strom 4: x=100, y=0, š=150, v=100, šSt/šKm= 3, vSt/vKo=5\n" + "\nPozice, rozměry a poměry kmen a korun souhlasí?")); strom1.setRozměr( 50, 30 ); strom2.setRozměr( 50, 40 ); strom3.setRozměr( 40, 40 ); strom4.setRozměr( 30, 50 ); assertTrue( P.souhlas(

1 Pokud by byl při konstrukci stromu nastaven např. poměr výšky 3 a celková velikost stro-

mu 8, měla by mít koruna velikost 6 a kmen 2. Když bychom pak chtěli změnit velikost stromu příště, spočetli bychom z velikosti koruny a kmen poměr výšky 4.

Page 275: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 275

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 275 z 433

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

"Strom_5 1: x=0, y= 0, š=50, v=30, vSt/vKo=3, šSt/šKm=10\n" + "Strom_5 2: x=0, y=150, š=50, v=40, vSt/vKo=4, šSt/šKm= 5\n" + "Strom_5 3: x=100, y=150, š=40, v=40, vSt/vKo=2, šSt/šKm=20\n" + "Strom_5 4: x=100, y=0, š=30, v=50, vSt/vKo=5, šSt/šKm= 3\n" + "\nPozice, rozměry a poměry kmen a korun souhlasí?")); strom1.setRozměr( 100, 150 ); strom2.setRozměr( 100, 150 ); strom3.setRozměr( 200, 200 ); strom4.setRozměr( 150, 100 ); assertTrue( P.souhlas( "Strom 1: x=0, y= 0, š=100, v=150, šSt/šKm=10, vSt/vKo=3\n" + "Strom 2: x=0, y=150, š=100, v=150, šSt/šKm= 5, vSt/vKo=4\n" + "Strom 3: x=100, y=100, š=200, v=200, šSt/šKm=20, vSt/vKo=2\n" + "Strom 4: x=100, y=0, š=150, v=100, šSt/šKm= 3, vSt/vKo=5\n" + "\nPozice, rozměry a poměry kmen a korun souhlasí?")); }

Metoda nejprve přepne plátno na jemnější krok, abychom mohli zkontrolovat správné zobrazení malých instancí a zobrazí dialogové okno oznamující rozměry stromů z přípravku. Když je odsouhlasíte, nastaví pro každý ze stromů rozměry, které nejsou soudělné s jeho velikostí ani poměry výšky a šířky kmene ku celkové výšce či šířce. Po dalším odsouhlasením se pokusí uvést vše do původního stavu, který opět nechá odsouhlasit.

2. krok: Definice nových atributů

Prvním krokem by měla být vždy příprava testu. Názvy dalších kroků se ale budou při řešení různých úloh různit. Neberte je proto jako dog-ma, ale spíš jako vodítko, pro lepší orientaci v popsaném postupu.

Prozatím se při testech se stromy vytvořenými přípravkem následně nic neděje, protože tělo metody setRozměr(int,int) je prázdné. Zatím je ještě nemůžeme na-plnit, protože nemám dostatečné podklady pro tvorbu stromů. Ty si zatím nechá-vá konstruktor při konstrukci stromu pro sebe.

Když se zamyslíte nad tím, jak metodu setRozměr(int,int) definovat, jistě vás napadne, že potřebný kód je již definován v konstruktoru. Ten ale při vytvá-ření instancí koruny a kmene používá informace o vzájemných poměrech šířky a výšky kmene a celého stromu, které se nikde neuchovávají.

V dalším kroku proto definujeme oba poměry jako atributy instance, které necháme konstruktorem nastavit. Po úpravě program ihned znovu otestujeme, i když se nám nezdá, že bychom měli takovouto změnou běh programu ovlivnit.

Úpravy (jsou vysazeny tučně) si můžete prohlédnout v následujícím výpisu:

Page 276: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

276 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 276 z 433

1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16

private int podílVýškyKmene; private int podílŠířkyKmene; public Strom_5( int x, int y, int šířka, int výška, int podílVýškyKmene, int podílŠířkyKmene ) { this.podílVýškyKmene = podílVýškyKmene; this.podílŠířkyKmene = podílŠířkyKmene; int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene; int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = ( šířka - šířkaKmene) / 2; koruna = new Elipsa ( x, y, šířka, výškaKoruny, Barva.ZELENÁ ); kmen = new Obdélník( x+posunKmene, y+výškaKoruny, šířkaKmene, výškaKmene, Barva.ČERVENÁ ); }

I po tak malé opravě bychom měli nechat třídu znovu přeložit, abychom se ubez-pečili, že jsme opravu provedli správně.

Funkce konstruktoru se touto opravou nijak nezměnila, nicméně bychom ji měli pro jistotu otestovat spuštěním nějakého testu, který ji prověří – stačí např. natáhnout přípravek do zásobníku odkazů.

3. krok: Kopírování těla konstruktoru do těla metody Potřebné informace máme k dispozici, takže můžeme vytvořit tělo metody setRozměr. Zkopírujeme do ní nejprve tělo konstruktoru. Hned si ale někam po-znamenáme, že máme stejný kód na dvou místech a že tento problém budeme muset v budoucnu ještě řešit. 1 2 3 4 5 6 7 8 9

10 11 12

public void setRozměr(int šířka, int výška) { int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene; int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = ( šířka - šířkaKmene) / 2; AP.nekresli(); koruna = new Elipsa ( x, y, šířka, výškaKoruny, Barva.ZELENÁ ); kmen = new Obdélník( x+posunKmene, y+výškaKoruny, šířkaKmene, výškaKmene, Barva.ČERVENÁ ); AP.vraťKresli(); }

Nad zkopírovaným programem nebudeme nijak bádat a požádáme rovnou pře-kladač, aby jej přeložil.

Page 277: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 277

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 277 z 433

4. krok: Dočasné „odkonstatnění“ některých atributů Tentokrát se nám nepodaří program ani přeložit. Překladač nám totiž oznámí, že nemůže přiřadit hodnotu konstantě koruna. Toho se můžeme na chvilku zbavit tím, že z deklarace atributu koruna (a hned také i z deklarace atributu kmen) od-straníme modifikátor final. Musíme si to ale někam zapsat, abychom je tam po zprovoznění překladu nezapomněli vrátit.

5. krok: Definice potřebných lokálních proměnných Při dalším pokusu o překlad se dozvíme, že používáme proměnnou x, kterou jsme nedeklarovali. Víme také proč: konstruktor dostal příslušné hodnoty jako parame-tr. Tentokrát je však nebudeme deklarovat jako atributy, protože víme, že přísluš-né hodnoty můžeme získat pomocí přístupových metod getX() a gwetY(). Definujeme proto lokální proměnné x a y a inicializujeme je pomocí těchto metod. 1 2 3 4 5 6 7 8 9

10 11 12 13 14

public void setRozměr(int šířka, int výška) { int x = getX(); int y = getY(); int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene; int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = ( šířka - šířkaKmene) / 2; AP.nekresli(); koruna = new Elipsa ( x, y, šířka, výškaKoruny, Barva.ZELENÁ ); kmen = new Obdélník( x+posunKmene, y+výškaKoruny, šířkaKmene, výškaKmene, Barva.ČERVENÁ ); AP.vraťKresli(); }

6. krok: Odstranění tvorby nových instancí koruny a kmene Nyní nám již překladač náš kód přeloží. Vyzkoušíme test – a ejhle, i ten projde. Pustíme se tedy do našeho restu, kterým je zbytečné vytváření nových instancí koruny a kmene.

Místo toho, abychom vytvářeli novou korunu a kmen by mělo stačit, aby-chom změnili rozměr dosavadní koruny a kmene. Jak vidíme z programu, u kme-ne pak musíme vedle jeho rozměru změnit i jeho pozici. 1 2 3 4 5 6

public void setRozměr(int šířka, int výška) { int x = getX(); int y = getY(); int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene;

Page 278: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

278 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 278 z 433

7 8 9

10 11 12 13 14

int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = ( šířka - šířkaKmene) / 2; AP.nekresli(); koruna.setRozměr( šířka, výškaKoruny ); kmen.setPozice( x+posunKmene, y+výškaKoruny ); kmen.setRozměr( šířkaKmene, výškaKmene ); AP.vraťKresli(); }

7. krok: Vrácení koruny a kmene mezi konstanty Testy stále fungují, takže se můžeme pustit do dalšího kroku, kterým je opětné vrácení atributů koruna a kmen mezi konstanty.

8. krok: Vyvolání metody setRozměr(int,int) v konstruktoru Pořád nám ještě zbývá jeden poznamenaný problém, kterým je zdvojení stejného kódu v konstruktoru a v metodě setRozměr(int,int). V metodě kód potřebujeme, protože bychom jinak nedovedli splnit její poslání. Upravit ale můžeme konstruk-tor. Když je potřebný kód definován v těle metody, proč jej tedy nezavolat.

Budu se stále držet zásady, že kroky mají být opravdu jednoduché a do kon-struktoru proto pouze přidám volání metody setRozměr(int,int).

Možná vám tento krok bude připadat zbytečně malý, ale to nevadí. Nebojte se dělat malé kroky (a hned je otestovat). Zvýší to vaši jistotu ve stabilitu vytváře-ného kódu. 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16

public Strom_6c( int x, int y, int šířka, int výška, int podílŠířkyKmene, int podílVýškyKmene ) { this.podílVýškyKmene = podílVýškyKmene; this.podílŠířkyKmene = podílŠířkyKmene; int výškaKmene = výška / podílVýškyKmene; int výškaKoruny = výška - výškaKmene; int šířkaKmene = šířka / podílŠířkyKmene; int posunKmene = ( šířka - šířkaKmene) / 2; AP.nekresli(); koruna = new Elipsa ( x, y, šířka, výškaKoruny, Barva.ZELENÁ ); kmen = new Obdélník( x+posunKmene, y+výškaKoruny, šířkaKmene, výškaKmene, Barva.ČERVENÁ ); setRozměr( šířka, výška ); AP.vraťKresli(); }

9. krok: Odstranění zdvojeného kódu z konstruktoru Testy stále chodí, tak nemusíme přemýšlet o tom, jestli je vše v pořádku. Když chodí testy, je to v pořádku (jsou-li dobře napsané).

Page 279: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 279

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 279 z 433

Podíváme se tedy do konstruktoru, co můžeme vyhodit. Začal bych ale raději tím, co vyhodit nemůžeme, a tím je vytvoření instancí koruny a kmene a přiřazení odkazů na ně do příslušných atributů.

Při vytváření těchto instancí však používám řadu pomocných proměnných, jejichž hodnoty jsme před tím počítali. Protože ale víme, že rozměr těchto instancí vzápětí nastavíme, stačí, když je při vytvoření pouze správně umístíme. Navíc stačí správně umístit pouze korunu, protože pozice kmene se stejně odvozuje z pozice a rozměru koruny.

Umístíme proto korunu do správné pozice, ale přiřadíme jí pro začátek veli-kost 1 bod. Totéž pak uděláme i se kmenem. Protože se přitom nic nekreslí (kres-lení máme na doby tvorby stromu potlačeno), tak nás toto zdánlivě zbytečné dvojité umisťování koruny a kmene prakticky nezdrží.

Po všech těchto úpravách tedy získáme následující podobu konstruktoru na-šeho stromu: 1 2 3 4 5 6 7 8 9

10 11

public Strom_6c( int x, int y, int šířka, int výška, int podílŠířkyKmene, int podílVýškyKmene ) { this.podílVýškyKmene = podílVýškyKmene; this.podílŠířkyKmene = podílŠířkyKmene; AP.nekresli(); koruna = new Elipsa ( x, y, 1, 1, Barva.ZELENÁ ); kmen = new Obdélník( x, y, 1, 1, Barva.ČERVENÁ ); setRozměr( šířka, výška ); AP.vraťKresli(); }

10. krok: Doplnění metody setRozměr(Rozměr) Když jsme vítězně dokončili zavedení metody setRozměr(int,int), nic nám ne-brání zavést také metodu setRozměr(Rozměr): 1 2 3 4

public void setRozměr(Rozměr rozměr) { setRozměr( rozměr.šířka, rozměr.výška ); }

11. krok: Doplnění metody setOblast(Oblast) Posledním krokem celé operace bude přidání metody setOblast(Oblast). Tam by-chom neměli zapomenout na to, že při akcích rozložených do několika kroků je výhodné vypnout překreslování plátna.

Page 280: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

280 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 280 z 433

1 2 3 4 5 6 7

public void setOblast( Oblast oblast ) { AP.nekresli(); setPozice( oblast.x, oblast.y ); setRozměr( oblast.šířka, oblast.výška ); AP.vraťKresli(); }

Výslednou podobu třídy Strom i testovací třídy najdete v projektu 06_Rozhraní_Z v souborech Strom_6c a Strom6cTest.

Nyní vám již nic nebrání zadat, že třída Strom implementuje rozhraní INafukovací a definovat test, který vyzkouší její spolupráci s instancemi třídy Kompresor.

6.10 Projekt Výtah Na závěr kapitoly si zkuste vyřešit další samostatný projekt. Zadání je následující:

Definujte třídu Výtah, která má za úkol simulovat provoz výtahu. In-stance třídy bude schopna jezdit nahoru a dolů v zadaném sloupci ak-tivního plátna. Na požádání bude umět přijet do zadaného patra,naložit pasažéra (grafický obrazec) a odvézt jej do požadovaného patra a tam jej vyložit do některého se sousedních sloupců. Prozatím se omezte na výtah pro jediného pasažéra.

Zadání jsem se pokusil udělat jako rozumný kompromis mezi obecným (např. „simulujte provoz výtahu“) a konkrétním, popisujícím detailně jednotlivé poža-davky.

Zkuste si nejprve udělat analýzu toho, jaké by měla mít konstruovaná třída vlastnosti jaké by měla mít definované metody (tj. na jaké zprávy by měla umět reagovat) a pak čtěte dál a porovnejte si svůj přístup se vzorovým řešením.

Page 281: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 281

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 281 z 433

Chcete-li mít před analýzou trochu jasnější představu o tom, co se pováš požaduje, zkopírujte si z projektu 06_Rozhraní_Z třídy VýtahX a VýtahXTest. Spuštěním testu obsluhy si můžete přehrát malou animaci, která by vám mohla naznačit, co po vás vlastně chci.

Ti odvážnější se mohou pokusit hned definovat celou třídu Výtah i s potřebnými testy. Pro ty lenivější jsem připravil kostru třídy s předdefinovanými prázdnými metodami, které odvodíme v následující analýze. Můžete je proto ihned spustit aniž by testy začínaly poukazováním na to, že chcete použít neexistující metody.

Těm netrpělivým bych znovu poradil: chcete-li se opravdu něco naučit, ne-nahlížejte do definic a zkuste podle následujícího textu nejprve definovat třídu sami Vzorové řešení použijte pouze v případě, když vám něco nebude chodit a nebudete si vědět rady.

Analýza problému Před tím, než začneme programovat, bychom se měli zamyslet nad tím, co po tří-dě a jejích instancích budeme chtít. Na jaké zprávy mají umět reagovat a které me-tody by proto měly poskytovat.

Okolí Náš výtah se bude pohybovat na aktivním plátně. Ze zadání víme, že výtah se bude pohybovat v jednom z jeho sloupců. Ten budeme považovat za výtahovou šachtu. Asi by bylo rozumné považovat za patra budovy jednotlivá řady políček.

Aby byl výtah výtahem, musí být schopen něco vozit. Náš výtah bude vozit pasažéry, což budou grafické objekty. Protože se budou pasažéři s výtahem pře-souvat (a dopředu tušíme, že je budeme přesouvat přesouvačem), měly by im-plementovat rozhraní IPosuvný. Kdykoliv budeme proto někde potřebovat deklarovat pasažéra (většinou to bude parametr), budeme deklarovat objekt typu IPosuvný.

Pro zjednodušení budeme skrytě předpokládat, že pasažér je definován tak, že se vejde do jednoho políčka aktivního plátna a že jeho cílové souřadnice bude-me moci nastavit jako souřadnice levého horního rohu políčka, do kterého jej bu-deme chtít přesunout.

Konstruktory Třídu jsme tedy zasadili do jejího budoucího okolí. Podívejme se nyní na to, jak bychom měli vytvářet její instance. Navrhněme, jaké budeme potřebovat kon-

Page 282: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

282 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 282 z 433

struktory, co vše jim musíme říci, aby pro nás vytvořily potřebný výtah, a jaké zá-kladní vlastnosti by měl čerstvě zkonstruovaný výtah mít.

Začnu od konce. Čerstvě zkonstruovaný výtah by se měl zobrazit jako čtverec zabírající právě jedno políčko aktivního plátna a měl by být umístěn v přízemí, tj. ve spodním políčku svého sloupce.

Zkusíme definovat dva konstruktory: první bude nejjednodušší možný, dru-hý umožní nastavit některé další vlastnosti vytvářeného výtahu.

Po přehrání výše zmíněné testovací animace bychom se asi mohli shodnout na tom, že i nejjednoduššímu konstruktoru bychom měli zadat alespoň sloupec, v němž bude výtah jezdit. Tento konstruktor by tak měl mít hlavičku

public Výtah( int sloupec )

Pokusme se nyní odvodit, o co bychom měli doplnit rafinovanější konstruktor. Při shlédnutí animace vás jistě napadne, že by měl umožňovat nastavit barvu výtahu. Druhá věc, které jste si možná všimli, je, že každý z výtahů jezdil jinou rychlostí. Měli bychom proto „lepší“ konstruktor doplnit i možností nastavit rychlost výta-hu. Raději si už dál nebudeme vymýšlet a dohodneme se, že jeho hlavička bude:

public Výtah( int sloupec, int rychlost, Barva barva )

Potřebné metody Položme si nyní otázku: „Co musí výtah umět?“ a odvoďme z ní, na jaké zprávy by měl reagovat a jaké metody bychom proto měli definovat.

Když chcete někam jet výtahem, musíte jej napřed přivolat. Aby k vám ale mohl dojet, musí vědět, ve kterém patře jste. Jednou z možností je předat ve zprávě budoucího pasažéra jako parametr, podle kterého výtah pozná, ve kterém patře se nachází. Ke splnění tohoto požadavku definujeme metodu

public void přjeďK( IPosuvný pasažér )

Když výtah přijede, musí pasažér nastoupit. Rozšiřovat ale třídu pasažéra a schopnost nástupu do výtahu není optimální. Lepší bude, naučíme-li výtah nabrat nového pasažéra. Definujeme proto metodu

public void nástup( IPosuvný pasažér )

Pasažér je tedy ve výtahu. Nyní bychom potřebovali, aby výtah uměl s pasa-žérem přijet do požadovaného patra. Definujeme proto metodu:

public void doPatra( int patro )

Všimněte si, že této metodě již nepředáváme pasažéra jako parametr. Pasažér již nastoupil. Výtah jej nabral a měl by proto vědět, koho veze.

Page 283: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 283

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 283 z 433

V cílové stanic by měl výtah vysadit svého pasažéra. Problém je, na kterou stranu jej budeme vysazovat. Na tu, ze které nastoupil? Můžeme jej ale také chat vybrat. Uděláme to šalamounsky tak, že doplníme třídu dvěma meto-dami“ jednou pro výstup vpravo a druhou pro výstup vlevo:

public void výstupVpravo() public void výstupVlevo()

Když jsme pěkně krok za krokem prošli celý postup převozu pasažéra z vý-chozí do cílové pozice, tak by asi bylo šikovné, kdybychom mohli celý proces realizovat jediným příkazem, po němž by výtah přijel pro pasažéra, naložil jej a odvezl do požadovaného patra, kde by jej také vyložil. K tomu mu stačí vě-dět, kdo jej žádá o převoz a kam touží dojet. Aby bylo možno vystupovat v cíli na obě strany, definujeme opět dvě metody:

void odvezVpravo( IPosuvný pasažér, int patro ) void odvezVlevo ( IPosuvný pasažér, int patro )

Implementace Hrubou analýzu máme za sebou, takže máme základní představu o tom, co a jak by měla naše třída a její instance dělat. To ale neznamená, že přesně takový celý projekt na konci bude. V průběhu implementace se může ukázat, že některá naše rozhodnutí ve fázi analýzy nebyla optimální, protože jsme si neuvědomili některé skutečnosti, které nám dojdou až při implementaci. Musíte být proto připraveni na to, že všechno se může změnit. Takže vzhůru na implementaci!

Abych vám ušetřil práci, připravil jsem vám v projektu 06_Rozhraní_Z tří-dy Výtah a VýtahTest, které obsahují předdefinované prázdné verze konstruktorů a výše popsaných metod.

Obě třídy jsou navrženy tak, že jdou hned přeložit. V následujícíchřádcích vás pomalu provedu postupem, jak tuto třídu „rozchodit“. Ne-budu ale již explicitně ukazovat, jak by se dalo to či ono naprogramo-vat, ale budu věci spíše naznačovat, abyste si je mohli naprogramovatsami.

Octnete-li se náhodou ve stadiu zoufalství, kdy si nebudete vědětrady, můžete se podívat na vzorové řešení ve třídě VýtahX.

Page 284: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

284 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 284 z 433

Implementovaná rozhraní Nejprve bychom se měli zamyslet nad základní charakteristikou třídy. Víme, že třída má na animačním plátně simulovat výtah. Musí jej tedy umět zobrazit a k tomu nutně potřebuje implementovat rozhraní IKreslený.

Výtah by se měl pohybovat, a to pokud možno plynule. K tomu bychom mohli využít přesouvače. Abychom mohli využít přesouvače, musí třída imple-mentovat rozhraní IPosuvný.

Doplňte hlavičku předpřipravené třídy Výtah o deklarace implementace po-třebných rozhraní a doplňte (pro začátek alespoň prázdné) definice implemento-vaných metod a výslednou třídu přeložte.

Atributy Nyní bychom se měli zamyslet nad potřebnými atributy. Na základě zkušeností se stromem a vašimi grafickými objekty vás jistě napadne, že instance potřebuje mít jako atribut odkaz na obrazec, který bude reprezentovat výtah. Tímto obraz-cem by měl být obdélník, který je velký jako jedno pole aktivního plátna a bude na počátku umístěn při spodním okraji plátna.

Otázkou je, jestli je výhodnější si sloupec, v němž se výtah nachází, pamato-vat v nějakém konstantním atributu (konstantním, protože lze očekávat, že by se v průběhu života výtahu neměl měnit), anebo zda si jej vždy zjistíme od obdélníku představujícího výtah. Rozhodnutí nechám na vás – vyberte si, co je vám sympa-tičtější.

Obdobné rozhodnutí vás čeká i v otázce patra, v němž se výtah zrovna na-chází. I tady si můžete vybrat, jestli pro ně vyčleníte atribut (tentokrát již ne kon-stantní), nebo jestli se na ně vždy zeptáte výtahu.

Dalším atributem výtahu by měl být asi přesouvač, kterým se výtah nechává elegantně přesouvat z aktuální pozice do pozice požadované a který může použít i pro přesun nastupujících a vystupujících osob. Určitě uznáte, že by asi nebylo moudré vytvářet pro každý požadovaný přesun nový přesouvač – výhodnější bude, když přesouvač bude vytvořen konstruktorem a instance pak při každém přesunu použije ten samý.

Kdyby všechny výtahy jezdily stejně rychle, mohly by používat společný pře-souvač, který by tak mohl být definován jako atribut třídy. Chceme-li však umož-nit, aby každý výtah jezdil svojí vlastní rychlostí, musí mít každá instance svůj vlastní přesouvač.

Doplňte definice konstruktorů tak, aby inicializovaly potřebné atributy a spuštěním testu Inicializace ověřte, že se výtahy správně nakreslí a umístí do výcho-zích pozic. Trochu vám napovím: aby se výtah ne plátně skutečně nakreslil, bude-te muset doplnit definici těla dříve definované metody nakresli(Graphics2D).

Page 285: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 285

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 285 z 433

Postup při návrhu metod Konstruktory a atributy máme za sebou. To sice ještě neznamená, že časem nezjis-tíme, že svá rozhodnutí budeme muset změnit, ale prozatím je budeme považovat za konečná. Podívejme se proto na řadové metody.

Při jejich implementaci nemusíme přesně dodržovat pořadí, v němž jsme je navrhli ve fázi analýzy. Rozumným hlediskem pro výběr příští definované meto-dy je to, zda bude za současného stavu definic možné definici metody otestovat. Kromě toho je samozřejmě rozumné definovat nejdříve metody, o nichž se do-mníváme, že je budeme chtít použít v definici jiných metod.

Seznam metod navržených při analýze začínala metodou přijeďK(IPosuvný). Při prohlídce zbývajících metod ale asi každého napadne, že v definici této meto-dy by se nám mohla hodit metoda doPatra(int). Definujte tedy nejprve ji.

Metoda doPatra(int) Metoda realizující příjezd do požadovaného patra přebírá cílové patro jako para-metr. Její definice by měla být jednoduchá:

Pamatujeme-li si v nějakém atributu patro, v němž se výtah právě nachází, stačí násobit rozdíl pater výškou patra (tj. velikostí kroku aktivního plátna) a o tento počet bodů přesunout obdélník představující výtah. Po dojetí do cíle je třeba si v atributu zapamatovat příslušné patro.

Nemá-li výtah uloženo patro, v němž se nachází, zjistí si svislou pozici cílo-vého patra a nechá se přesunout do této cílové pozice.

Definujte tělo metody doPatra(int) a spuštěním testu PrázdnýVýtahDoPatra ověřte, že výtahy se pohybují požadovaným způsobem.

Metoda přijeďK(IPosuvný) Když již umíme dojet s výtahem do požadovaného patra, mohli bychom jej naučit i přijet ke svému pasažérovi. K tomu stačí zjistit patro, v němž se nachází pasažér, a nechat výtah do tohoto patra dojet.

Patro pasažéra zjistíme např. tak, že spočteme celočíselný podíl jeho svislé souřadnice a velikosti pole aktivního plátna. Tak se dozvíme řádek, v němž se na-chází. Odečtením tohoto řádku zvednutého od počtu řádků plátna zjistíme podla-ží. Výsledek musíme zmenšit o jedničku, protože přízemí je považováno za nulté podlaží.

Definujte tělo metody přijeďK(IPosuvný) a spuštěním testu PřijeďK ověřte, že výtah skutečně přijede, kam má.

Page 286: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

286 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 286 z 433

Metoda nástup(IPosuvný) Nástup pasažéra bude velmi jednoduchý. Naprogramujeme jej tak, že požádáme přesouvač, aby pasažéra přesunul na pozici výtahu.

Musíme však zařídit, aby pasažér při nastupování vyvolal dojem, že vchází do výtahu a vykresloval se proto před tím, než se vykreslí výtah. Dosáhneme toho tak, že před tím, než pasažéra předáme přesouvači, aby s ním „nastoupil“, požá-dáme aktivní plátno zavoláním metody přidejPod(IKreslený,IKreslený), aby pa-sažéra umístilo pod výtah.

Této metodě však nemůžeme předat pasažéra jako parametr, protože není typu IKreslený. My sice víme, že je, ale překladač to neví – pro něj objekty typu IPosuvný nejsou typu IKreslený. Budeme si proto muset pomoci operátorem pře-typování (viz kapitola Operátor přetypován (typ) na straně 188), takže nazveme-li parametr metody pasažér, bude mít volání metody tvar:

AP.přidejPod( výtah, (IKreslený)pasažér );

Definujte tělo metody nástup() a spuštěním testu NástupVýstup ověřte, že pasažéři do výtahu skutečně nastoupí. Test sice skončí s chybou, protože pasažéři nevy-stoupí, ale tu napravíme hned v následujícím kroku.

Metody výstupVpravo() a výstupVlevo() Když už jsme naučili pasažéra nastoupit, tak bychom jej měli také naučit vystou-pit. Víme, že pro výstup z výtahu jsme si naplánovali metody dvě: výstupVpravo() a výstupVlevo(). Podívejme se tedy na ně.

Začneme např. s výstupem vpravo. Na první pohled se zdá nejjednodušší prostě nechat pasažéra přesunout o šířku políčka doprava. Problém je, že nemáme pasažéra. Budeme muset definovat potřebný atribut (nazvěme jej třeba pasažér) a pak upravit metodu nástup(IPosuvný), aby do tohoto atributu uložila odkaz na nastoupivšího pasažéra.

Upravte tedy metodu nástup(IPosuvný) a znovu pusťte test NástupVýstup, abys-te se přesvědčili, že jste nic nepokazili.

Pak definujte tělo metody výstupVlevo() a znovu pusťte test. Teď už by měl výstup vlevo chodit.

Doplňte symetricky metodu výstupVpravo() a znovu spusťte prověřovací test. Předpokládám, že stále bude vše v pořádku.

Test převozu pasažéra Vyzkoušejte test PřevozPasažéra. Pokud jste vše naprogramovali tak, jak jsem to na vás narafičil, měl by se před vámi nyní objevit problém: pasažér sice do výtahu nastoupí, ale výtah odjede bez pasažéra.

Page 287: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 287

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 287 z 433

Jedním z možných řešení je pasažéra po nástupu odhlásit ze seznamu objektů obhospodařovaných aktivním plátnem, takže se pak přestane na plátno kreslit plátno to po něm přestane chtít). Před výstupem jej budeme muset opět přihlá-sit, ale to už naše metoda dělá.

Upravte tedy metodu nástup(IPosuvný) a zkuste znovu test PřevozPasažéra. Po-kud jste opět udělali to, co jsem vám poradil, měl by nyní výtah sice v pořádku odjet, ale při vystupování by se měl pasažér objevit na poli, z nějž před chvílí vý-tah odjel, a odtud se přesouvat do cílové výstupní pozice.

Už asi víte, kde je chyba – pasažéra jsme měli před přihlášením k plátnu nej-prve zavoláním setPozice(int,int) přemístit na políčko, na němž se zrovna na-chází výtah. Opravte proto znovu metodu výstupVlevo() a prověřte opravu testem.

Nyní bychom měli provést totéž i s metodou výstupVpravo(), ale to by se nám už mohlo začít zdát, že se příliš velká část kódu opakuje na několika místech. Mohli bychom to napravit např. tak, že bychom definovali soukromou pomocnou metodu výstup(int), které bychom předali jako parametr počet bodů, a o něž se bude pasažér přesouvat – metoda výstupVlevo() by zadávala zápornou hodnotu a metoda výstupVpravo() hodnotu kladnou.

Definujte novou metodu, přesuňte do ní kód z těla metody výstupVlevo() a upravte ji tak, aby velikost vodorovného přesunu přebírala jako parametr. Pak upravte metody výstupVlevo() a výstupVpravo() tak, aby využily jejích služeb. Upravený program opět otestujte.

Metody odvezVpravo(IPosuvný,int) a odvezVlevo(IPosuvný,int) Zbývají nám poslední dvě metody a poslední předpřipravený test. Definice těchto souhrnných metod by ale měla být pro vás již hračkou: zavoláte pasažérovi výtah, necháte jej nastoupit, odvezete jej do požadovaného patra a tam jej necháte vy-stoupit zadaným směrem.

Opět ale máme dvě metody, který mají většinu kódu stejnou, takže bychom opět mohli definovat nejprve pomocnou soukromou metodu, která realizuje po-třebnou činnost až po odvoz do zadaného patra a původní metody již pouze při-dají výstup požadovaným směrem.

Naprogramujte nyní vše potřebné a celý program vyzkoušejte testem Odvez. Pokud jste někde nezazmatkovali, měl by test proběhnout bez problémů.

Jedním z možných řešení je definovat pro pasažéra speciální atribut a po jeho nástupu do tohoto atributu umístit odkaz na nastoupivšího pasažéra. Protože pa-sažér je typu IPosuvný, bude muset být i tento atribut typu IPosuvný.

Tím se ale problému nezbavíme, protože pasažéra budeme muset také na-kreslit. Jinými slovy: budeme potřebovat, aby metoda nakresli mohla nakreslit

Page 288: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

288 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 288 z 433

nejprve obdélník výtahu a na něm převáženého pasažéra. Jenomže na aktivní plátno můžeme kreslit pouze pomocí kreslítka, ale objekty typu IPosuvný nám nic takového nezaručí.

My naštěstí víme, že náš posuvný objekt bude určitě implementovat také rozhraní IKreslený.

Metoda nakresli() pak bude mít na starosti nakreslení výtahu následované nakreslením pasažéra. Tím ale hned vyvoláme další problém: překladač odmítne pasažéra nakreslit, protože není typu IKreslený (my víme, že je, ale on to neví).

V projektu 06_Rozhraní_Z najdete ještě dvě další verze výtahových tříd.

Třída VýtahD nepředvádí nástup pasažéra z boku, ale zepředu. Pa-sažér se v ní viditelně přesune na pozici výtahu, který za ním po-tom zavře dveře.

Třída VýtahP pracuje podobně jako předchozí třída, ale tento výtahnemá dveře, takže pasažér je po celou dobu přesunu vidět.

Spusťte test Odvez ve třídách VýtahDTest a VýtahPTest, prohlédněte si chování těchto modifikovaných výtahů a pokuste se definovat odpoví-dající modifikované verze sami.

6.11 Shrnutí – co jsme se naučili Seznámili jsme se s novou sadou grafických tříd, jejímž jádrem je třída AktivníPlátno, která pracuje jako manažer a je schopna zabezpečit korektní vykreslení všech jí svěřených grafických tvarů.

Vedle klasických tříd, které definují metody, jež opravdu něco dělají, zavádí Java ještě speciální třídy označované jako rozhraní, které pouze deklarují po-žadované vlastnosti a neobsahují žádnou implementaci. V těle rozhraní jsou proto uvedeny pouze hlavičky deklarovaných metod ukončené středníkem.

Rozhraní definujeme obdobně jako třídu, pouze v hlavičce uvedeme místo klíčového slova class klíčové slovo interface.

Všechny metody deklarované v rozhraní jsou veřejné, a to i v případě, kdy u nich není explicitně uveden modifikátor public.

Rozhraní může být implementováno standardní třídou (class). Třída může implementovat libovolný počet rozhraní.

Page 289: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 6: Rozhraní 289

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 289 z 433

Implementaci rozhraní definujeme v prostředí BlueJ natažením implementač-ní šipky od implementující třídy k implementovanému rozhraní.

Třída, která implementuje rozhraní, se musí k této implementaci přihlásit tím, že v hlavičce za svým názvem uvede klíčové slovo implements následované seznamem názvů implementovaných rozhraní.

Třída, která implementuje nějaké rozhraní, musí implementovat (tj. defino-vat) všechny metody deklarované v těle rozhraní.

Instance třídy implementující nějaká rozhraní se může vydávat za instanci kteréhokoliv z implementovaných rozhraní.

Potřebujeme-li, aby program považoval instanci nějakého typu za instanci ji-ného typu, můžeme ji přetypovat. Operátor přetypování pak za běhu zkont-roluje, je-li požadované přetypování korektní.

Potřebujeme-li definovat vlastnosti nějaké skupiny objektů, např. parametrů metody, můžeme definovat rozhraní, v němž požadované vlastnosti deklaru-jeme. Všechny třídy, u jejichž objektů budeme chtít mít zaručené dané vlast-nosti, pak budou toto rozhraní implementovat.

Návrhový vzor Služebník (Servant) použijeme tehdy, budeme-li potřebovat definovat nějakou službu, která bude pracovat s instancemi různých typů. Definujeme proto rozhraní, v němž specifikujeme požadované vlastnosti těchto objektů, a příslušné služební metody budou mít mezi svými parametry uveden objekt daného typu.

Refaktorování je postup, při němž v drobných krocích upravujeme náš pro-gram tak, aby se zlepšila jeho architektura a tím se usnadnila jeho případná další vylepšení.

Základem refaktorování je zásada, že úpravy musejí probíhat opravdu v ma-lých krocích a po každém kroku se znovu spustí testy ověřující, že jsme tímto krokem nenarušili funkčnost programu.

Návrhový vzor Stav použijeme tehdy, budou-li se reakce objektů dané třídy výrazně lišit v závislosti na stavu, v němž se objekty právě nacházejí. Při jeho realizaci definujeme nové, stavové rozhraní popisující stavově závislou část objektu.

V definici multistavové třídy deklarujeme atribut zastupující stavově zá-vislou část jejích objektů jako odkaz na toto stavové rozhraní. Zároveň defi-nujeme skupinu stavových tříd implementujících ono stavové rozhraní, přičemž každá z těchto tříd bude specifikovat chování objektu v jednom z

Page 290: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

290 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 290 z 433

možných stavů. Přepínání mezi stavy realizujeme voláním metod vracejících instanci jiné

stavové třídy.

Nové termíny

Page 291: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 291

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 291 z 433

7. Co takhle něco zdědit?

Kapitola 7 Co takhle něco zdědit?

☯ Co se v kapitole naučíme V této kapitole se seznámíme s možností, aby jedno rozhraní dědilo od jiného rozhraní. Vysvětlíme si, jaké jsou mezi rozhraními rodičovské vztahy a co to znamená, když je jedno rozhraní potomkem druhého. Ukážeme si také, že rozhraní může mít i několik rodičů a co z toho ply-ne.

V druhé části kapitoly se seznámíme s návrhovým vzorem Zástupce a na závěr zkusíte dotvořit aplikaci, která tento návrhový vzor používáa která simuluje provoz kabinek na uzavřené lince tvořené řadou zastá-vek.

V této kapitole nebudeme otevírat nový projekt. Místo toho budeme pokračovat ve vylepšování projektu z minulé kapitoly. Konečnou po-dobu celého projektu jakož i některé mezistupně najdete v projektu07_Dědění_rozhraní_Z.

V poznámce šťouralům na str. 270 jsem říkal, že přesouvač u svých instancí skrytě předpokládá, že implementují rozhraní IKreslený (přesouvat plynule objekt, který není vidět, přeci nemá smysl). Definicí takového chování jsem ovšem naprogra-moval něco, co může vést při běhu programu k havárii, protože se klidně může stát, že někdo definuje třídu, která bude implementovat rozhraní IPosuvný, ale ne-bude implementovat rozhraní IKreslený. Přitom to vůbec nemusí být autovým úmyslem – stačí, když se jen zapomene o implementaci rozhraní zmínit v hlavič-ce.

Jestli si ještě vzpomínáte na dělení chyb do tří skupin (viz kapitola Ladění na straně 95), tak si možná vybavíte nepříjemnou vlastnost takových chyb: i když nás program na chybu upozorní, neumí nám říct, kde jsme ji udělali. Řekne nám pou-

Page 292: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

292 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 292 z 433

ze, kde na ni přišel. A to může být úplně jinde, než tam, kde chyba vnikla a kde je ji třeba opravit. (Uvedený příklad to ostatně názorně dokazuje.)

Potřebovali bychom, aby bylo možno překladači sdělit, že má hned při volání metody zkontrolovat, že instance implementuje obě rozhraní. V této kapitole se budeme zabývat jednou z možností, jak toho dosáhnout – využijeme toho, že roz-hraní mohou od sebe dědit.

7.1 Co to je, když rozhraní dědí? Dědění rozhraní je věc naprosto jednoduchá: když při konstrukci jednoho rozhra-ní (říkejme mu dceřiné) dospějeme k závěru, že by třídy implementující toto roz-hraní měly současně implementovat i jiné rozhraní (budu mu říkat rodičovské), oznámíme prostě v hlavičce dceřiného rozhraní, že toto rozhraní rozšiřuje (ex-tends) rodičovské rozhraní a tím pro nás práce končí.

Tím, že oznámíme, že dceřiné rozhraní rozšiřuje rodičovské rozhraní, zaří-díme, že do portfolia metod dceřiného rozhraní přibudou k deklarovaným meto-dám i metody rodičovského rozhraní. O těchto metodách říkáme, že je dceřiné rozhraní od svého rodiče zdědí. (Dceřiné rozhraní je přitom může, ale také nemu-sí uvést ve své definici.)

Kdokoliv chce dceřiné rozhraní implementovat, musí vedle jeho „vlastních“ metod implementovat i metody zděděné. Instance tříd, které implementují nějaké dceřiné rozhraní, se proto mohou vydávat i za instance jeho rodičovského (v pří-padě delší dědické posloupnosti i prarodičovského, praprarodičovského apod.) rozhraní, protože z principu dědičnosti musí mít jeho metody implementovány.

Kdykoliv se pak nějaká třída přihlásí k implementaci dceřiného rozhraní, překladač zkontroluje, že implementuje jak metody deklarované v dceřiném roz-hraní, tak metody jejího rodičovského rozhraní. Náhodný překlep, o kterém jsem hovořil na počátku kapitoly, se nám tak podaří přesunout z běhových chyb mezi chyby syntaktické, tj. mezi ty, které se nejsnadněji hledají a opravují.

Podívejme se na náš příklad s geometrickými tvary. Jak jsem říkal před chvílí, přesouvat plynule objekt, který není vidět, nemá smysl. (Neviditelné obrazce se mohou klidně přesunout skokem, protože to stejně nikdo nepozná.) Rozhraní IPouvný proto definujeme jako potomka (mohli bychom také říci rozšíření) roz-hraní IKreslený.

Předhodíme-li nyní přesouvači objekt typu IPosuvný, budou všichni vědět, že by případné umístění tohoto objektu na plátno mělo proběhnout bez problémů. Přesouvač již nebude muset daný objekt při umísťování na plátno přetypovávat, protože instance typu IPosuvný jsou zároveň instancemi typu IKreslený.

Page 293: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 293

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 293 z 433

7.2 Jak to zařídit Teď už nám zbývá se pouze naučit dědičnost rozhraní realizovat. V prostředí BlueJ je postup jednoduchý: natáhnete dědickou šipku (použijete stejné tlačítko, jako při natahování implementační šipky, tj. šipku s trojúhelníkovým hrotem) od dceřiného rozhraní k rodičovskému rozhraní. Vyzkoušejte si to a natáhněte šipku do rozhraní IPosuvný k rozhraní IKreslený.

Ve zdrojovém kódu definujeme dědění od nějakého rodičovského rozhraní tak, že do hlavičky dceřiného rozhraní přidáme za název rozhraní klíčové slovo extends následované názvem děděného rozhraní. V případě rozhraní IPosuvný te-dy BlueJ upraví po natažení dědické šipky jeho hlavičku do tvaru:

interface IPouvný extends IKreslený

Obrázek 7.1 Po doplnění dědičnosti rozhraní je náš diagram třídy zbytečně „přešipkovaný“

Page 294: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

294 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 294 z 433

Po této úpravě samozřejmě zneplatní nejenom kód nově nastaveného dceřiného rozhraní, ale i kód všech tříd, které je implementovaly nebo používaly ve svých metodách parametry daného typu, ale to vás asi nepřekvapilo. Stačí stisknout tla-čítko Přeložit a vše bude za chvilku opět připraveno k použití.

Jak jistě odhadujete, je možný i obrácený postup, tj. zanesení informace o dě-dičnosti přímo do zdrojového kódu dceřiného rozhraní, na které BlueJ po překla-du (nebo uložení) zareaguje natažením příslušné dědické šipky od tohoto rozhraní k deklarovanému rodičovskému rozhraní. Vyzkoušejte si to na rozhraní INafukovací.

Duplicitně deklarovaná implementace Tak máme v našem projektu (obr. 7.1) opět poněkud „přešipkováno“ a je nejvyšší čas to napravit. Na začátku této kapitoly jsme si říkali, že každá třída, která im-plementuje nějaké rozhraní, automaticky implementuje i všechny jeho rodiče. A toho teď využijeme.

Vezmete-li implementační a dědické šipky jako ukazatel toho, za čí instance se mohou instance třídy, z níž vede šipka, vydávat, zjistíte, že k rozhraní IKreslený vedou od „šipkujících tříd“ dvě nebo tři šipkové cesty: jedna přímá, druhá vedoucí přes rozhraní IPosuvný a u některých tříd ještě třetí vedoucí přes rozhraní INafukovací. Nic se tedy nestane, když „přímou šipku“ smažeme.

U všech tříd našeho projektu, které implementují rozhraní IPosuvný nebo INafukovací tedy můžeme klidně smazat implementační šipku vedoucí k rozhraní IKreslený. Tím se nám obrázek přeci jenom trochu zpřehlední (bohužel ne nadlouho) – viz obr. 7.2.

7.3 Společný potomek několika rozhraní

Když jsme spolu probírali návrhové vzory, tak jsem vám na konci povídání o je-dináčkovi zadal úkol naprogramovat třídu ČernáDíra (najdete jej na str. 235), která přitáhne a spolkne zadaný obrazec. Tenkrát jsme ono přitahování a polykání mu-seli realizovat trhaně. Nyní, když mám k dispozici přesouvače a kompresory mů-žeme snadno dosáhnout toho, že bude celý proces probíhat elegantně a plynule.

Pokusme se nyní vymyslet, jak definovat třídu ČernáDíra2, aby byl celý pro-ces nejenom plynulý, ale abychom také nemuseli pro každý typ polykaných ob-jektů definovat vlastní metodu.

Page 295: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 295

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 295 z 433

Černá díra objekty napřed přitáhne a potom spolkne. Aby je mohla plynule přitáhnout za pomoci přesouvače, potřebuje, aby objekty implementovaly rozhra-ní IPosuvný, aby je následně mohla spolknout za pomoci kompresoru, potřebuje aby implementovaly rozhraní INafukovací.

Obrázek 7.2 Vzhled diagramu tříd po odstranění nadbytečných šipek

Jak jistě tušíte (ostatně nadpis podkapitoly vám to napověděl), pomůžeme si tak, že definujeme rozhraní, které bude potomkem obou dvou. Nazveme je např. IHýbací. Jeho definice bude superjednoduchá (ani ji nebudu umísťovat do rámeč-ku:

interface IHýbací extends IPosuvný, INafukovací {}

Všimněte si, že toto rozhraní má prázdné tělo – nedeklaruje žádnou vlastní meto-du. Spokojí se totiž s tím, co zdědilo. Jinými slovy: kdokoliv se rozhodne imple-mentovat rozhraní IHýbací, musí implementovat metody deklarované v

Page 296: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

296 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 296 z 433

rozhraních IPosuvný a INafukovací (a tím pádem i v rozhraní IKreslený). Nic víc se po něm nechce.

Pokud vás někoho napadlo, že druhá černá díra je vlastně služebník (i když to u černé díry zní divně), máte pravdu. I tady jsme si nejprvevymysleli (zanalyzovali), co potřebujeme a pak jsme nadefinovali roz-hraní, které tyto naše potřeby deklaruje. Nyní nám zbývá připravit tes-ty a pak vše implementovat a prozkoušet.

Přidejte nyní rozhraní IHýbací do projektu a natáhněte všechny dědické a implementační šipky. V druhém kole pak zopakujte proces s odstra-ňováním duplicitních implementačních šipek, jak jsme to dělali předchvílí. Když ještě trochu přeskupíte třídy v diagramu a přidáte třídu ČernáDíra_7 spolu s její testovací třídou, měl by výsledný diagram vy-padat podobně jako na obr. 7.3.

Page 297: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 297

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 297 z 433

Obrázek 7.3 Projekt připravený pro naprogramování třídy ČernáDíra2

Takže si zopakujeme, co vlastně od nové černé díry chceme:

stejně jako v minulé verzi má být jedináček,

nabízet metodu getČernáDíra(), která vrátí odkaz na tohoto jedináčka, což je černý kruh usazený v prostředku plátna,

instance bude mít metodu spolkni(IHýbací), která objekt získaný jako para-metr přesune nad černou díru a tam jej zmenší a nakonec odstraní z plátna (spolkne).

Když jsem pro vás připravoval tuto úlohu, uvědomil jsem si, že původní kompre-sor uměl měnit rozměr svěřené instance pouze tak, že neměnil její pozici. Naše dosavadní instance, jejichž pozice byla pozicí levého horního rohu opsaného ob-délníku se proto zvětšovaly vpravo dolů a zmenšovaly vlevo nahoru.

Kompresor to doposud nemohl dělat jinak, protože při nějakém elegantněj-ším zmenšování by potřeboval také měnit pozici daného objektu a to mu instance

Page 298: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

298 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 298 z 433

rozhraní INafukovcí nezaručovaly. Když ale nyní máte k dispozici i rozhraní IHýbací, tak jsem pro vás možnosti kompresoru rozšířil.

Stáhněte si z projektu 07_Dědění_rozhraní_Z novou verzi třídy PKompresor(=posouvací kompresor), jejíž instance jsou bohatší o přetížené verze metod, která očekává, že nafukovaný, resp. vyfukovaný parametr bude typu IHýbací a umožňuje zadat, kterým směrem je kotvící bod, který sepři změně rozměru nebude hýbat. Zadáte-li jako parametr směru null, bude se zmenšovat do středu.

7.4 Návrhový vzor Stav (State) Všechna rozhraní, která jsme doposud v našich programech používali, realizovala návrhový vzor Služebník. Sloužila k tomu, abychom definovali požadované vlastnosti objektů, kterým může některá třída (AktinvíPlátno, Přesouvač, Kompresor) nabízet svoje služby.

Použití rozhraní je však daleko širší. Jednomu z nich se budeme věnovat v té-to podkapitole. Povíme si o návrhovém vzoru Stav, při kterém rozhraní specifikuje objekty, které mohou být v různých stavech. Reakce těchto objektů na zprávy pak závisí na tom, v jakém stavu se zrovna daný objekt nachází.

Projekt Šipky Představte si např. jednoduchou šipku, po které bychom chtěli, aby uměla reago-vat na tři zprávy, tj. aby měla implementované tři metody:

nakresli(Kreslítko) nakreslí šipku dodaným kreslítkem do příslušného po-líčka plátna,

vpřed()přesune šipku na sousední políčko ve směru, do kterého je natočena,

vlevoVbok() otočí šipku o 90° vlevo.

Začnete-li se zamýšlet nad tím, jak byste potřebné metody naprogramovali, zjistí-te, že podoba programu se bude lišit v závislosti na tom, do kterého směru je šip-ka právě natočena. Klasicky orientovaní programátoři by problém řešili asi tak, že by si vždy zjistili, do kterého směru je šipka právě natočena, a podle toho vybrali příslušnou reakci. My však nejsme programátoři klasičtí, ale objektově orientova-

Page 299: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 299

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 299 z 433

ní, a jako takoví máme mnohem širší možnosti (navíc jsem vám záměrně ještě ne-vysvětlil, jak se na něco zeptat a podle odpovědi se rozhodnout).

Kdybyste věděli, do kterého směru je šipka natočena, jistě by pro vás nebyl problém definovat metody nakresli(Kreslítko) ani vpřed(). Metoda vlevoVbok() by sice byla trochu pracnější, ale určitě byste ji vymysleli také.

Problém je ale v tom, že by se šipka poté, co se otočí, měla začít chovat úplně jinak. Prohlásíme-li směr, co nějž je šipka natočena, za její stav, tak otočením šipky měníme její stav a tím i veškeré její chování.

Má-li šipka jiné chování, je rozumné uvažovat o tom, že bude instancí jiné třídy – zvolme pro tyto třídy např. názvy typu XŠipka, v nichž bude první písme-no označovat světovou stranu, do které jsou jejich instance natočeny, tj. názvy VŠipka, SŠipka, ZŠipka a JŠipka. Tyto třídy budou vytvářet jednostavové instance specializované pro pohyb v jednom směru.

Teď ještě musíme vymyslet, jak to zařídit, aby se tyto čtyři třídy tvářili nave-nek jako třída jediná. Jednou z možností je definovat rozhraní (budu je v dalším textu označovat jako stavové rozhraní), které budou všechny čtyři specializované jednostavové třídy implementovat, a své instance pak vydávat za instance tohoto rozhraní. Toto stavové rozhraní bychom mohli v našem případě nazvat třeba IŠipka.

Naše řešení však stále není dokonalé, protože pořád ještě nemáme vymyšle-no, jak to zařídit, aby se při otáčení nahrazovala jedna šipka druhou. Mohli by-chom to vyřešit např. tak, že by naše specializované jednostavové třídy definovaly místo metody vlevoVbok() metodu doleva(), která by vracela odkaz na jednosta-vovou šipku specializovanou pro pohyb v novém směru. Pro univerzální, několi-kastavovou šipku bychom pak definovali samostatnou třídu Šipka, která by jako svůj atribut uchovávala odkaz na tu správnou specializovanou jednostavovou šipku a při žádosti o otočku by svůj atribut požádala, aby ji za sebe poskytl pří-slušného nástupce.

Abychom mohli do tohoto atributu uložit kteroukoliv ze specializovaných ši-pek, musíme jej definovat jako odkaz na instanci typu IŠipka, protože to je typ, za jehož instanci se může kterákoliv ze specializovaných jednostavových šipek vy-dávat.

Po zakomponování všech těchto úvah do programu by definice třídy Šipka mohla vypadat následovně: 1 2 3 4 5 6 7

public class Šipka implements IPosuvný { //== KONSTANTY TŘÍDY =========================================================== private static final AktivníPlátno AP = AktivníPlátno.getPlátno(); //== ATRIBUTY INSTANCÍ =========================================================

Page 300: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

300 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 300 z 433

8 9

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

IŠipka šipka; //== KONSTRUKTORY A METODY VRACEJÍCÍ INSTANCE VLASTNÍ TŘÍDY ==================== public Šipka(int x, int y, Barva barva) { šipka = new VŠipka( x, y, barva ); } //== PŘÍSTUPOVÉ METODY ATRIBUTŮ INSTANCÍ ======================================= public int getX() { return šipka.getX(); } public int getY() { return šipka.getY(); } public void setPozice(int x, int y) { šipka.setPozice( x, y ); } //== PŘEKRYTÉ METODY IMPLEMENTOVANÝCH ROZHRANÍ ================================= public void nakresli(Kreslítko g) { šipka.nakresli( g ); } //== NOVĚ ZAVEDENÉ METODY INSTANCÍ ============================================= public void vlevoVbok() { šipka = šipka.doleva(); AP.překresli(); }//public void vlevoVbok() public void vpřed() { šipka.vpřed(); } }//public class Šipka

Stavové rozhraní IŠipka byste jistě dokázali nadefinovat sami. Pro jistotu ale jeho možnou definici uvedu také: 1 2

public interface IŠipka extends IPosuvný {

Page 301: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 301

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 301 z 433

3 4 5

void vpřed(); IŠipka doleva(); }//public interface IŠipka

Definice jednostavových tříd specializovaných pro pohyb v jednom směru budou prakticky shodné. Např. třída VŠipka, jejíž instance se pohybují na východ, by mohla vypadat následovně: 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

public class VŠipka implements IŠipka { //== KONSTANTNÍ ATRIBUTY TŘÍDY ================================================= private static final AktivníPlátno AP = AktivníPlátno.getPlátno(); //== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================ private Trojúhelník špička; private Obdélník tělo; //############################################################################## //== KONSTRUKTORY A METODY VRACEJÍCÍ INSTANCE VLASTNÍ TŘÍDY ==================== public VŠipka( int x, int y, Barva barva ) { int krok = AP.getKrok(); int k2 = krok/2; špička = new Trojúhelník( x*krok + k2, y*krok, k2, krok, barva, Směr.V ); tělo = new Obdélník( x*krok, y*krok + krok/4, k2, k2, barva ); } //== PŘÍSTUPOVÉ METODY ATRIBUTŮ INSTANCÍ ======================================= public int getX() { return tělo.getX(); } public int getY() { return špička.getY(); } public void setPozice(int x, int y) { int krok = AP.getKrok(); špička.setPozice( x + krok/2, y ); tělo .setPozice( x, y + krok/4 ); } //== PŘEKRYTÉ METODY IMPLEMENTOVANÝCH ROZHRANÍ ================================= public void nakresli(Kreslítko g) {

Page 302: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

302 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 302 z 433

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

špička.nakresli( g ); tělo.nakresli( g ); } //== NOVĚ ZAVEDENÉ METODY INSTANCÍ ============================================= public IŠipka doleva() { int krok = AP.getKrok(); return new SŠipka( getX()/krok, getY()/krok, špička.getBarva() ); } public void vpřed() { setPozice( getX()+AP.getKrok(), getY() ); } }//public class VŠipka

Odsuneme-li z diagramu tříd všechny ostatní třídy někam stranou, mohl by dia-gram tříd naší šestice vypadat např. tak, jako no obr. 7.4:

Obrázek 7.4 Diagram tříd podílejících se přímo na definici šipek

(je zapnuto zobrazování šipek závislostí i dědičnosti)

Oproti tomu, s čím jsme se setkávali doposud, má tento diagram jednu zajímavou vlastnost: cyklickou závislost specializovaných jednostavových tříd. Jejich instan-

Page 303: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 303

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 303 z 433

ce si při otáčení vlevo postupně předávají odpovědnost za správné vykreslení a chování šipky. Proto závisí první třída na druhé (metoda doleva() vrací její in-stanci), druhá na třetí, třetí na čtvrté a to opět na té první.

Shrnutí Třídy v diagramu ještě trochu přemístíme a odstraníme šipky zobrazující vzájem-né závislosti specializovaných jednostavových tříd. Ponecháme pouze jedinou zá-vislost vedoucí od vícestavové třídy Šipka k stavovému rozhraní IŠipka. (viz obr. 7.5). Získáme tak diagram který názorně zobrazuje architekturu návrhového vzo-ru stav.

Obrázek 7.5 Diagram lépe demonstrující možnou realizaci vzoru Stav

Z toho, co jsme se doposud dozvěděli a co jsme si vyzkoušeli, bychom mohli se-psat následující zásady použití návrhového vzoru Stav:

1. Návrhový vzor Stav použijeme tehdy, nacházejí-li se objekty nějaké třídy v různých stavech, které se liší svými reakcemi na zasílané zprávy.

2. V definici třídy popisující takovéto objekty a jejich chování (tzv. mnohastavo-vá třída) deklarujeme speciální atribut, který bude zastupovat část objektu, v níž jsou soustředěny stavově závislé vlastnosti objektů. Tento atribut specifi-kujeme jako instanci nového (stavového) rozhraní, které definujeme v násle-dujícím kroku.

Page 304: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

304 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 304 z 433

3. Definujeme stavové rozhraní, v němž deklarujeme metody odpovídající zprávám, na něž reagují instance multistavové třídy rozdílně v závislosti na svém stavu. Spolu s těmito metodami musíme někdy deklarovat i další me-tody, kterou jsou potřebné pro komunikaci mezi multistavovou třídou a jejím jednostavovým atributem.

Mezi deklarovanými metodami mohou být i takové, které vracejí odkaz na instanci jiné jednostavové třídy a pomáhají tak realizovat změnu stavu.

4. Po každý stav definujeme speciální jednostavovou třídu implementující toto rozhraní. Metody specifikující změnu stavu budou vracet odkaz na instanci jiné stavové třídy.

Projekt Robot

Definice všech tříd realizujících společně implementaci šipky naleznete v projektu 06_Rozhraní_Z. Stáhněte si je odtud a prozkoumejte jejich cho-vání.

Definujte třídu Robot mající podobné vlastnosti jako výše definovanátřída Šipka. Tato třída bude definovat půdorysný obrázek robota, kterýbude rozumět povelům krok() a vlevoVbok(). Obrázek robota navrhně-te tak, aby z něj bylo zřejmé, do kterého směru je robot natočen. Pro in-spiraci vám nabízím několik nápadů mých žáků:

7.5 Návrhový vzor Zástupce (Proxy)

Pro tuto podkapitolu si z projektu 07_Dědění_rozhraní_Z zkopírujte do svého projektu rozhraní IZastávka a IMultiposuvný a třídy Testkabina, Kabina, KabinaRUP, Linka a Multipřesouvač.

Již několikrát jsme si říkali, že jednou z největších vymožeností objektově oriento-vaného programování je schopnost zapouzdření implementačních detailů před všemi, kdo je nepotřebují znát.

Page 305: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 305

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 305 z 433

Někdy se ale dostáváme do situace, kdy potřebujeme, aby instance vytvářené třídy měla řadu schopností (tj. aby uměla reagovat na řadu zpráv), ale většina z těchto schopností by měla sloužit pouze pro interní potřebu komunikace s nejbliž-šími spolupracovníky a před budoucími „koncovými uživateli“ by je bylo lepší zatajit, protože je za prvé nebudou potřebovat a za druhé by jejich nevhodným použitím mohli narušit některé důležité invarianty (= vlastnosti, které by se nemě-ly měnit).

Ukažme si to na příkladu, který jsem si pro vás připravil na závěr kapitoly a který má simulovat trať se zastávkami, po které jezdí kabiny lanovky (nebo něče-ho jiného). Abyste o tomto příkladu získali lepší představu, spusťte si ve třídě KabinaTest test KabinaRUP. Mělo by se zobrazit aktivní plátno o rozměrech 7×7 po-lí, na němž budou tři soustředné různobarevné linky, po kterých jezdí číslované kabinky – viz obr. 7.6.

Obrázek 7.6 Tři soustředné linky s pohybujícími se číslovanými kabinami

Na vytvoření linky se podílí dvě třídy: Linka a Zastávka. Linka je vlastně cyklická posloupnost zastávek (cyklická proto, že za poslední zastávkou plynule následuje první). Aby mohla linka zařadit zastávku do svého seznamu, musí se zastávka umět napojit na svého budoucího předchůdce a následníka. Tuto svou schopnost však musí před ostatními třídami utajit, aby jí nějaký „nekalý živel“ nemohl po-žádat o začlenění do jiné linky, protože by kvůli tomu přetrhla posloupnost zastá-vek na své současné lince.

Page 306: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

306 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 306 z 433

Samozřejmě, že bychom mohli definovat další metodu, která by ko-rektně vyjmula zastávku z jedné linky a začlenila ji do druhé, jenomže to nechceme. Naopak, chceme takovéto operaci zabránit.

Linka to zařídí rafinovaně: definuje zastávku uvnitř sebe tak, aby to nikdo nevi-děl. Pokud ji někdo požádá o odkaz na zastávku (např. proto, že potřebuje znát je-jí souřadnice, aby k ní mohl přijet), tak mu odkaz sice předá, ale předá mu jej jako instanci rozhraní IZastávka, takže bude možno volat pouze ty metody, které jsou v rozhraní deklarovány. Ostatní metody nebudou dostupné, protože z hlediska překladače instance rozhraní IZastávka žádné jiné metody nezná a nemůžeme je tedy volat. Překladač totiž nezajímá, že daná instance má implementovány ještě další metody, než ty, které jsou deklarovány v rozhraní. Pro něj instance rozhraní umí pouze to, co rozhraní deklaruje.

Rozhraní IZastávka nám umožní pouze několik operací: jeho instance nám bude ochotna prozradit na které lince se daná zastávka nachází, jaké má souřad-nice a která zastávka jí předchází a která za ní následuje. O jakékoliv další mani-pulace se zastávkou (např. její vyjmutí z linky) budeme muset požádat její linku, která si ohlídá, aby byla zastávka po vyjmutí z linky nepoužitelná a nemohla být využita k narušení linky.

Instance rozhraní IZastávka tak slouží jako odstiňující zástupce skutečné in-stance. Podle toho také dostal tento návrhový vzor svůj název.

Existují i jiné způsoby realizace odstiňujícího zástupce a existují i jinédruhy zástupců. Nelekněte se proto, pokud se někdy setkáte s imple-mentací tohoto návrhového vzoru, která bude vypadat trochu jinak ařešit jiný trochu problém.

Základní idea zástupce je, že aplikace dostane místo plnohodnotné-ho původního objektu k dispozici jeho zástupce, který předá všechnypožadavky aplikace zastupovanému objektu a výsledky, které zastupo-vaný objekt vrátí, předá naopak aplikaci.

7.6 Projekt Kabina Na závěr kapitoly jsem si pro vás zase připravil jeden (doufejme, že zajímavý) projekt. Využijeme před chvílí rozebíranou simulaci provozu kabin na linkách a zkusíte si vytvořit vlastní třídu Kabina, jejímiž instancemi budou kabiny pohybují-cí se po linkách. Než vás ale „vypustím do světa“, abyste vše naprogramovali,

Page 307: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 307

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 307 z 433

proběhneme spolu nejprve nejdůležitější vlastnosti tříd a instancí, které budete ve svém projektu používat.

Předpřipravené třídy

Multipřesouvač Instance této třídy jsou vylepšenými přesouvači. Předáte-li multipřesouvači objekt k přesunu, nezdrží se v něm program po celou dobu, než se objekt přesune, ale multipřesouvač vrátí řízení volajícímu programu hned a zatímco se program za-bývá svými záležitostmi, multipřesouvač přesouvá svěřený objekt.

Klidně mu můžete vzápětí předat další objekt a on bude přesouvat oba na-jednou. Předáte-li mu rychle za sebou k přesunu třeba dvacet objektů, bude se na plátně pohybovat všech dvacet objektů najednou.

O přesun objektu, který musí implementovat rozhraní IPosuvný požádáte multipřesouvač voláním metody přesuň, která má čtyři přetížené verze: dvěma z nich předáváte celočíselné souřadnice cílové pozice a dalším dvěma pak předává-te cílovou pozici jako objekt třídy Pozice.

Při zadávání přesunu si můžete vybrat, zadáte-li dobu, za kterou má přesou-vaný objekt dosáhnout cílové pozice a nebo jakou rychlostí se má k cíli pohybo-vat. Dobu přesunu zadáváte jako reálné číslo udávající počet sekund, rychlost zadáváte jako celé číslo udávající počet pixelů, o něž se objekt za sekundu posune.

Nesmíte zapomenout na to, že se multipřesouvač orientuje podle typu zada-ných dat. Budete-li tedy doba přesunu celé číslo (např. 2 sekundy), musíte ji pře-typovat na double, protože jinak ji bude multipřesouvač považovat za rychlost.

Vedle metod které odstartují přesun zadaného objektu nabízí multipřesouvač i metodu zastav(IPosuvný), kterou multipřesouvač žádáte o předčasné ukončení přesunu zadaného objektu. Protože multipřesouvač ví, že program nemusí vždy umět zjistit, jestli je dotyčný objekt ještě přesouván, nerozčílí se, když mu zadáte objekt, s nímž nehýbá, ale prostě neudělá nic.

Multipřesouvač má dva konstruktory: první s celočíselným parametrem za-dávající periodu opakování mikropřesunů svěřených objektů (zadává se v mili-sekundách) a druhý bezparametrický, který tuto periodu nastavuje na 50 ms, což znamená, že se obraz objektů překreslí asi 20krát za sekundu. Máte-li pomalý po-čítač, můžete tuto periodu zvětšit.

IMultiposuvný Rozhraní IMiltiposuvný je potomkem rozhraní IPosuvný a rozšiřuje množinu jím vyžadovaných metod o metodu přesunuto(Multipřesouvač). Jak jsem již řekl, mul-

Page 308: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

308 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 308 z 433

tipřesouvač je ochoten přemísťovat jakýkoliv IPosuvný objekt. Pokud ale zjistí, že přesouvaný objekt je instancí třídy implementující rozhraní IMultiposuvný, při-praví pro něj zvláštní bonus: po skončení přesunu mu tuto skutečnost oznámí právě zavoláním metody přesunuto(Multipřesouvač), přičemž se při volání sám předá jako parametr.

To je od multipřesouvače chytrý marketingový tah. Instance implementující rozhraní IMultiposuvný totiž budou určitě pěkní cestovatelé a lze proto předpo-kládat, že po přesunu do první cílové pozice zatouží po přesunu do další. A koho o to požádají? No přece multipřesouvač, který získali jako parametr. Nebudou se přece pídit po jiném, když jednoho již mají v dosahu a z minulého přesunu s ním mají jistě dobré zkušenosti.

IZastávka Jak jsem se již zmiňoval ve výkladu rozhraní Zástupce, instanci typu IZastávka vrací linka při žádosti o odkaz na svoji první zastávku. Instance tohoto rozhraní vám umějí prozradit svoji polohu, linku, ve které jsou začlenění, a předat odkaz na zastávku, která jim předchází a která za nimi následuje.

Linka Již jsme si říkali, že linka je cyklickou posloupností svých zastávek. Chceme-li ji vytvořit, musíme jí zadat její barvu a souřadnice nejméně dvou jejích budoucích zastávek. Různé linky musí mít různou barvu, abychom na plátně snadno pozna-li, kudy která linka vede v případě, že budou dvě stanice nad sebou, jako je tomu například u Metra.

Linka nabízí dva konstruktory: jeden požaduje vedle barvy linky ještě čtyři celá čísla představující souřadnice prvých dvou zastávek linky. Druhý pak poža-duje vedle barvy pouze jeden parametr, kterým je objekt obsahující informace o souřadnicích nejméně dvou (ale může jich být libovolně mnoho) prvních zastávek vytvářené linky.

Druhý konstruktor nechte prozatím mně – vy s ním budete umět pra-covat až zvládnete práci s poli, kterým bude věnována kapitola Pole na straně 403.

K vytvořené lince můžete přidávat další stanice pomocí metody přidejZa(IZastávka,int,int), které v prvním parametru předáte odkaz na stanici, za kterou se má přidávaná stanice zařadit, a v dalších dvou parametrech jí pak

Page 309: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 309

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 309 z 433

předáte souřadnice přidávané stanice. Metoda pak vrátí odkaz na právě přidanou zastávku.

Abychom mohli nějakou zastávku přidat, musíme ale získat odkaz na jakou-koliv zastávku, která již na lince je. K tomu slouží metoda getPrvní(), která vrátí odkaz na první zastávku dané linky. Jak jsme si řekli před chvílí, od každé za-stávky pak můžeme získat odkaz na jejího předchůdce a následníka.

Zastávku na lince můžeme samozřejmě i odstranit – k tomu souží metoda odstraň(IZastávka). Kromě toho můžeme odstranit celou linku zavoláním meto-dy zruš().

Vedle vytváření a rušení zastávek nám linka umožňuje také zjišťovat a nasta-vovat doporučenou rychlost kabin na lince a doporučenou dobu, po kterou se má kabina zdržet ve stanici než vyrazí směrem k další stanici.

Navíc třída definuje tři statické metody, které vytvoří předem připravené pě-tiúhelníkové linky, a nimiž můžete následně experimentovat.

Úloha – Kabina Prostředí jsme tedy podrobně rozebrali a můžete si s ním pohrát. Můžete vytvářet linky a přidávat k nim zastávky a pak zase zastávky odebírat a můžete i zkoušet posílat několik posuvných předmětů současně multipřesouvačem.

Až si vyhrajete a seznámíte se s podpůrnými třídami, pokuste se o řešení ná-sledujícího úkolu: Definujte třídu Kabina, jejíž instance budou simulovat kabiny pohybující se po výše popsaných linkách. Třída a její instance budou mít následu-jící vlastnosti:

Třída bude definovat konstruktor Kabina(Linka), kterému předáme linku, na jejíž počáteční zastávku konstruktor kabinu po jejím vytvoření usadí a vypus-tí ji po trati.

Kabiny se budou po trati pohybovat doporučenou rychlostí a budou v za-stávkách čekat doporučenou dobu.

Instance bude definovat metodu skonči(), po jejímž zavolání bude kabina odebrána z trati a smazána z plátna.

Odebranou kabinu již nebude možno vrátit na trať, tj. nebude nutno defino-vat metody, které by to umožňovaly.

Nápověda: Aby se kabina mohla pohybovat po členité trati linky, měla by třída Kabina implementovat rozhraní IMultiposuvný (a tím i všechny tímto rozhraním vyžadované metody) a využívat plně možností nabízených třídou Multipřesouvač.

Page 310: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

310 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 310 z 433

Při prvních složitějších úkolech jsem vás vedl krok za krokem „za ruku“. V minu-lé kapitole jsem již pouze stručně popsal řešení. Nyní vás zkusím vypustit do svě-ta, abyste na správné řešení přišli sami. Není tak složité, takže věřím, že to dokážete i bez nahlížení do vzorového řešení ve třídě KabinaRUP.

Page 311: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 311

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 311 z 433

Page 312: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

312 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 312 z 433

Všechny potřebné třídy včetně testovací třídou a prázdné třídy Kabinaurčené pro vaše řešení jste si již stáhli na počátku podkapitoly Návrhovývzor Stav (State)

Všechna rozhraní, která jsme doposud v našich programech používali, realizovala návrhový vzor Služebník. Sloužila k tomu, abychom defi-novali požadované vlastnosti objektů, kterým může některá třída (Ak-tinvíPlátno, Přesouvač, Kompresor) nabízet svoje služby.

Použití rozhraní je však daleko širší. Jednomu z nich se budeme věnovat v této podkapitole. Povíme si o návrhovém vzoru Stav, při kte-rém rozhraní specifikuje objekty, které mohou být v různých stavech.Reakce těchto objektů na zprávy pak závisí na tom, v jakém stavu sezrovna daný objekt nachází.

Projekt Šipky Představte si např. jednoduchou šipku, po které bychom chtěli, abyuměla reagovat na tři zprávy, tj. aby měla implementované tři metody:

nakresli(Kreslítko) nakreslí šipku dodaným kreslítkem do pří-slušného políčka plátna,

vpřed()přesune šipku na sousední políčko ve směru, do kterého je natočena,

vlevoVbok() otočí šipku o 90° vlevo.

Začnete-li se zamýšlet nad tím, jak byste potřebné metody naprogra-movali, zjistíte, že podoba programu se bude lišit v závislosti na tom,do kterého směru je šipka právě natočena. Klasicky orientovaní pro-gramátoři by problém řešili asi tak, že by si vždy zjistili, do kteréhosměru je šipka právě natočena, a podle toho vybrali příslušnou reakci.My však nejsme programátoři klasičtí, ale objektově orientovaní, a jako takoví máme mnohem širší možnosti (navíc jsem vám záměrně ještěnevysvětlil, jak se na něco zeptat a podle odpovědi se rozhodnout).

Kdybyste věděli, do kterého směru je šipka natočena, jistě by provás nebyl problém definovat metody nakresli(Kreslítko) ani vpřed().Metoda vlevoVbok() by sice byla trochu pracnější, ale určitě byste ji vy-mysleli také.

Problém je ale v tom, že by se šipka poté, co se otočí, měla začítchovat úplně jinak. Prohlásíme-li směr, co nějž je šipka natočena, za její stav, tak otočením šipky měníme její stav a tím i veškeré její chování.

Má-li šipka jiné chování, je rozumné uvažovat o tom, že bude in-stancí jiné třídy – zvolme pro tyto třídy např. názvy typu XŠipka, v nichž bude první písmeno označovat světovou stranu, do které jsou je-ji h i t t č tj á VŠi k SŠi k ZŠi k JŠi k T t

Page 313: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 7: Co takhle něco zdědit? 313

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 313 z 433

7.7 Shrnutí – co jsme se naučili Rozhraní může dědit od jiného rozhraní. Rozhraní, které dědí, označujeme za potomka nebo dceřiné rozhraní, rozhraní od kterého se dědí označujeme za rodičovské rozhraní.

Dceřiné rozhraní přebírá do svého „portfolia“ všechny metody deklarované v jeho rodičovském rozhraní.

Potřebujeme-li použít v metodě parametr, který bude implementovat několik rozhraní, můžeme vytvořit rozhraní, které bude společným potomkem všech těchto rozhraní.

Třída implementující dceřiné rozhraní musí implementovat metody jeho i všech jeho rodičů.

Deklaruje-li několik rodičovských rozhraní stejnou metodu, je považována za jednu a tu samou metodu a implementující třídy ji implementují pouze jed-nou.

Návrhový vzor Zástupce (Proxy) použijeme v případě, kdy potřebujeme pře-dat uživatelům nějaké instance, avšak chceme jim zpřístupnit pouze některé z jejích vlastnosti. Při použití tohoto vzoru nedáme uživatelům k dispozici tří-du, jejíž instance budou používat, ale pouze jejího zástupce, což může být např. rozhraní, které daná třída implementuje. Toto ochranné rozhraní (ochranný zástupce) zabezpečuje, že se uživatelé nemohou dostat k metodám dané třídy, které nemohou být soukromé, ale přitom není žádoucí, aby k nim měli uživatelé přístup.

Nově zavedené termíny Termín

Page 314: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

314 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 314 z 433

8. Třídy mohou také dědit

Kapitola 8 Třídy mohou také dědit

☯ Co se v kapitole naučíme V této kapitole si ukážeme, že dědit od sebe mohou nejenom rozhraní, ale i třídy. Povíme si, čím se dědění tříd liší od dědění rozhraní, probe-reme jeho výhody i jeho slabiny a vysvětlíme si, za jakých podmínek je vhodné se pro dědění tříd rozhodnout.

Dědění tříd patří nejen k nejvíce využívaným, ale také k nejvíce zne-užívaným rysům objektově orientovaného programování. Proto si bu-deme povídat nejenom o tom, jak dědičnost používat, ale také si ukážeme, kdy bychom neměli podlehnout jejím svodům a měli bychom místo ní použít jiné řešení.

Tato kapitola bude asi pro mnohé z vás patřit k nejtěžším kapitolám učebnice (mně dala také zabrat ☺). Bude to i proto, že v ní nebudou praktické příklady, ale pouze takové, na nichž budu demonstrovat pro-bíranou látku. (V příští kapitole vám to vynahradím.)

Začneme vysvětlením základních principů dědění tříd po němž bu-de následovat jejich předvedení v jednoúčelovém projektu sloužícím pouze k této demonstraci. V další části nadefinujeme nejprve jednoduš-ší a poté i složitější potomky, a poté si ukážeme, jak je možné pro sku-pinu tříd se společnými vlastnostmi definovat také společného rodiče.

V závěrečných podkapitolách si vysvětlíme, jaká nebezpečí s sebou dědění tříd přináší a naučíme se základní pravidla, podle kterých se budeme rozhodovat, kdy dědění tříd použít.

Page 315: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 315

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 315 z 433

8.1 Podtřídy a nadtřídy

Specializace Pracujeme-li někde se skupinou osob, zvířat, věcí či vlastností, většinou záhy zjis-tíme, že v ní existují nejrůznější podskupiny se speciálními vlastnostmi a takže tu-to skupinu často nějak definujeme a pojmenujeme a potom v některých situacích pracujeme už pouze s touto podskupinou. , čímž ji vydělíme že by se nám hodilo definovat specielní třídu právě pro tuto podskupiny.

Ve škole nás např. učili, že obdélník, který má všechny strany stejně dlouhé má některé zvláštní vlastnosti, a aby se nám s ním lépe pracovalo, dostal i svůj název – čtverec. Obdobně bychom mezi elipsami mohli podmnožinu obrazců označovaných jako kruh, vegetariáni vydělují ze všech jídel ta, která neobsahují maso, a pouze taková jídla jsou ochotni konzumovat, běžný řidič se při výběru automobilu omezuje na osobní automobily a podobných příkladů byste jistě našli celou řadu.

Tím, že jsme z dané množiny prvků vydělili prvky se speciálními vlastnost-mi, ale nepřestaly tyto prvky patřit do své původní množiny. Tím, že čtverec do-stal vlastní jméno, ještě nepřestal patřit mezi obdélníky. Každý čtverec je současně i obdélníkem stejně tak, jako je každý kruh elipsou, i když trochu zvláštní. Vege-tariánské jídlo zůstává jídlem a osobní automobil automobilem. Možná vám to teď připadá samozřejmé, ale příliš mnoho programátorů na to zapomíná – ještě se k tomu vrátím.

Zobecnění Téměř stejně často se setkáváme i s obráceným postupem, při němž zkušenosti získané se skupinou zvláštních objektů zobecňujeme na rozsáhlejší skupinu obecnějších objektů nebo vytváříme novou skupinu, která slučuje několik do té doby samostatných skupin.

Zobecnění známých pravidel na rozsáhlejší skupinu objektů jste několikrát zažili v hodinách matematiky. V první třídě jste začali pracovat s přirozenými čís-ly. Tuto množinu jste pak rozšířili na čísla celá, poté na čísla racionální (zlomky) a ještě později na čísla reálná (sem patří např. číslo π nebo √2) a někteří z vás se již potkali i se závěrečným rozšířením na čísla komplexní.

Ze školy byste měli znát i zavedení nové skupiny, do které jste pak zařazovali věci, o nichž jste před tím uvažovali odděleně. Pro malé děti jsou např. čtverec, trojúhelník a kruh rozdílné útvary. V pozdějších letech se naučí, že to jsou vlastně

Page 316: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

316 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 316 z 433

speciální případy obecného geometrického tvaru, který je možné nakreslit či vy-modelovat a pro nějž je např. možné změřit a spočítat obvod či obsah.

Realizace v OOP Má-li objektové programování modelovat reálný svět, musí nabízet prostředky, jak tyto vlastnosti světa a jeho objektů zachytit. Jedním z nástrojů, které slouží k simulaci výše pospaných vlastností, je dědičnost tříd. Má-li nějaká množina in-stancí specifické vlastnosti, které je možno využít a/nebo naopak nutno respekto-vat, můžeme pro ni definovat zvláštní podtřídu, která zdědí všechny vlastnosti původní třídy a přidá ty, které popisují speciální vlastnosti dané podmnožiny.

Můžeme např. definovat podtřídu Čtverec, mezi jejíž instance budou patřit takové obdélníky, které budou mít za každé situace oba rozměry stejně veliké. Obdobně můžeme definovat třídu Kruh, do níž budou patřit elipsy s trvale stej-nými oběma rozměry.

Obdobné je to i při zobecňování. Máme-li několik tříd, jejichž instance vyka-zují nápadně podobné vlastnosti, můžeme jim definovat společnou nadtřídu, kte-ré bude na jednom místě definovat společné vlastnosti všech svých podtříd, a každá z jejích podtříd bude moci definovat pouze ty vlastnosti svých instancí, kte-rými se tyto instance liší od instancí ostatních podtříd společné nadtřídy.

Zůstaneme-li u našich geometrických tvarů, můžeme jim definovat společ-nou nadtřídu PosuvnéTvary, která bude definovat obecné vlastnosti posuvného tvaru (tj. má nějakou pozici a musí ji umět prozradit a nastavit a bude umět rea-govat na skupinu posunových zpráv).

Jiným příkladem by mohly být naše výtahy. Snad si ještě vzpomenete, že všechny tři ukázkové výtahy uměly prakticky totéž a lišili se pouze tím, jestli se za pasažérem zavíraly dveře a byl-li pasažér cestou ve výtahu vidět. Mohly by-chom proto definovat pro všechny tři třídy společnou nadtřídu, která by defino-vala základní vlastnosti a metody a jednotlivé třídy by pak pouze doplnily specifika nástupu pasažéra do výtahu a jeho zobrazení při převozu do zadaného patra.

Page 317: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 317

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 317 z 433

Terminologie související s dědičností tříd je bohatá, a každý autor dává přednost něčemu jinému. V této knize proto termíny trochu střídat, abyste si zvykli na všechny a zbytečně vás při čtení další literatury ne-překvapily.

Podtřída bývá mnohými autory označována jako odvozená třídanebo jako dceřiná třída. Výjimečně se setkáte i s termínem rozšířená třída, ale to většinou jen jako otrocký překlad. Já budu většinou použí-vat termín dceřiná třída, ale občas použiji také některý ze zbylých dvou.

Nadtřída bývá označována také jako základní nebo bázová třídaanebo jako rodičovská třída. Já budu v duchu dědičnosti většinou pou-žívat termín rodičovská třída, ale stejně jako u dceřiné třídy dávám dů-raz na to většinou.

Neustále mějte na paměti, že jednou z klíčových vlastností instancí dceřiných tříd je to, že se mohou kdykoliv vydávat za instance rodičovské třídy. Protože jsem se setkal se záplavou porušení této zásady, raději vám ji ještě vypíchnu:

Dceřiná třídy blíže specifikuje

vlastnosti podmnožiny instancí své rodičovské třídy. Instance dceřiné třídy musí být proto kdykoliv považovatelná

za instanci kterékoliv její rodičovské třídy. Přetypování na předka

nesmí být v rozporu s logickou aplikace

Znám několik rádobyobjektových programátorů (někteří z nich bohu-žel dokonce píší učebnice nebo učí na vysokých školách), kteří za zá-kladní vlastnost dceřiných tříd považují to, že dceřiná třída svýminstancím něco přidá, takže v jejich programech je např. úsečka neboobdélník potomkem bodu. Protože instanci jakékoliv třídy je možno kdykoliv přetypovat na předka, muselo by v těchto aplikacích být možno definovat obdélník, jehož vrcholy by byly tvořeny obdélníky čiúsečku jejímiž vrcholy by byly úsečky. Prosím vás, nezopakujte jejichchyby.

Univerzální (pra)rodič Na závěr tohoto obecného úvodu se vrátím ještě na počátek výkladu OOP, kde jsem tvrdil, že podle objektově orientované programování tvrdí, že všechno je ob-jekt. Z toho logicky vyplývá, že všechno musí být instancí třídy objektů. A sku-

Page 318: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

318 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 318 z 433

tečně. Java definuje třídu Object, která je rodičem (prarodičem, praprarodičem…) všech ostatních tříd. Třída Object je Adamem a Evou všech ostatních tříd.

Z předchozího také logicky vyplývá, že všechny ostatní třídy jsou něčí dcery. Ty, které nesehnaly nějakého „lepšího“ rodiče jsou dcerami třídy Object.

Mezi rodiče třídy se většinou počítají i její prarodiče. Zavedeme si proto termín vlastní nebo bezprostřední rodič třídy, který bude označovat je-jího nejbližšího rodiče, tj. rodiče, který není jejím prarodičem.

Budu-li v dalším textu hovořit o rodičích třídy, budu tím myslet jak její vlastní, bezprostřední rodiče, tak i případné prarodiče. Kdyby něcoplatilo pouze pro vlastní rodiče třídy a nevyplývalo to jasně z kontextu, výslovně to uvedu.

Obdobně často říkáme, že objekt x je instancí třídy X i tehdy, je-li tří-da X rodičem třídy, kterou jsme o vytvoření objektu x požádali. Zave-deme proto termín vlastní třída instance, který budu používat ve chvíli, kdy budu potřebovat, že se jedná o třídu, která danou instanci„porodila“ a ne o některého z jejích rodičů.

8.2 Experimenty s dědičností

☯ V této podkapitole vám na speciálně navržených třídách předvedu, jakbudou výše uvedené obecné zásady fungovat ve vašich programech. Postupně probereme všechny klíčové vlastnosti dědičnosti tříd, aby vám ve chvílích, kdy si nebudete některými aspekty vzájemného vzta-hu rodičovských a dceřiných tříd jisti, mohla tato podkapitola posloužitjako referenční příručka

Obávám se, že toto je jedna z nejtěžších podkapitol celé knihy. Do-poručuji vám proto k ní usednout odpočatí a svěží. Když vše hned na-poprvé nepochopíte, nevadí, můžete se k ní vrátit později, až se sprobíranými otázkami setkáte v dalších programech.

Než začneme používat dědičnost k řešení praktických úloh v našich programech, zkusíme ji nejprve chvilku zkoumat na skupině jednoduchých tříd, které mají je-diný účel: pomoci vám pochopit základní principy, mechanizmy a zákonitosti dě-dičnosti tříd.

Page 319: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 319

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 319 z 433

Pro naše experimenty s dědičností, jsem připravil samostatný projekt nazvaný 08_Dědičnost_tříd_pokusy (viz obr. 8.1), ve kterém jsou připraveny třídy, které vám mají pomoci si ujasnit, na co musíte být při používání dědičnosti ve svých programech připraveni a na co si máte dát pozor.

Obrázek 8.1 Pomocný projekt pro výklad dědičnosti

Univerzální rodič Object Jak jsem vám prozradil v minulé podkapitole, každá třída má svého rodiče. V Javě proto nemůžete definovat třídu, která by neměla žádného rodiče. Pokud jí žádné-ho nepřidělíte, přidělí jí překladač univerzálního rodiče, kterým je třída Object.

Já jsem tuto skutečnost před vámi doposud tajil, ale teď se vše provalilo. Přes-tože jsme žádné z našich tříd rodiče nepřiřazovali, všechny nějakého mají. Vyne-chám-li testovací třídy, jež mají speciálního rodiče, kterého jim přidělil BlueJ, pak všechny ostatní námi vytvořené třídy mají jako rodiče přiřazenu třídu Object.

O tom se ostatně můžete hned přesvědčit. Vytvořte zcela prázdnou třídu (na obrázku jsem ji nazval Pokusná) a nechte zkonstruovat její instanci. Místní nabíd-ka tohoto odkazu bude začínat příkazem zděděno z Object otevírajícím podnabídku se seznamem metod, které daná třída zdědila od třídy Object. Stejně začínají

Page 320: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

320 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 320 z 433

místní nabídky libovolného odkazu na libovolnou instanci (chcete-li se přesvědčit, otevřete kterýkoliv z předchozích projektů a vyzkoušejte to).

Většina zděděných metod je sice určena pro náročnější programátorské obra-ty, najdete však mezi nimi i metodu toString(), se kterou jsme již v našich pro-gramech pracovali a která vrací řetězcovou reprezentaci své instance.

S děděním od třídy Object je to podobné jako s konstruktory: také platí, že každá třída musí mít konstruktor, takže pokud programátor žádnýkonstruktor nedefinuje, doplní za něj překladač implicitní.

Obrázek 8.2 Seznam metod, které dědí každá třída od třídy Object

Atributy a bezparametrické konstruktory tříd v projektu Podívejme se nyní na to, co jsem pro vás v projektu připravil. Třídy Matka, Dcera a Vnučka jsou prozatím bezprostředními potomky třídy Object. Jak ale jistě odhad-nete, jsou určeny k tomu, abychom mezi nimi definovali rodičovské vztahy.

Tyto tři třídy jsou doprovázeny dvěma testovacími třídami. První z nich, tří-da BezDědičnosti, je „duševně připravena“ na to, že mezi třídami Matka, Dcera a Vnučka nemusí být žádné dědické vztahy. Můžete ji proto hned přeložit a dokonce

Page 321: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 321

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 321 z 433

se vám podaří úspěšně spustit první dva testy (další až do zavedení dědičnosti si-ce spustíte, ale neskončí úspěchem).

Třídu Sdědičností budete moci přeložit až poté, co zavedete dědičnost, pro-tože její metody s ní již počítají a překladač proto vyžaduje její správné nastavení.

Třídy Matka, Dcera a Vnučka jsou si velice podobné. Každá obsahuje tři sou-kromé atributy, které jsou inicializovány již v deklaraci:

atribut třídy x_počet (x zde zastupuje počáteční písmeno názvu třídy) obsa-hující počet vytvořených instancí,

atribut instance x_pořadí s pořadovými číslem dané instance v rámci třídy a

atribut x_název, který obsahuje název třídy následovaný pořadovým číslem příslušné instance.

Kromě toho definuje každá z těchto tříd bezparametrický konstruktor tisknoucí na standardní výstup zprávu o vytvoření dané instance. 1 2 3 4 5 6 7 8 9

10 11 12 13 14

public class Matka { private static int m_počet = 0; private int m_pořadí = ++m_počet; private String název = "Matka_" + m_počet; public Matka() { System.out.println( "\nVytvářím " + m_pořadí + ". instanci třídy Matka " ); } //... Metody třídy a jejích instancí }

Nejprve se podíváme, jak se třídy chovají před tím, než mezi nimi zavedeme pří-buzenské vztahy. Nemáte-li ještě přeloženou třídu BezDědičnosti, přeložte ji a nechte naplnit zásobník odkazů z přípravku. V okně standardního výstupu se ob-jeví text: Vytvářím 1. instanci třídy Matka Vytvářím 1. instanci třídy Dcera Vytvářím 1. instanci třídy Vnučka

Spustíte-li test nebo požádáte-li o naplnění zásobníku odkazů z přípravku podru-hé, bude vytvářet jejich druhé instance, poté třetí atd.

Page 322: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

322 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 322 z 433

Hierarchie dědičnosti Podívejme se nyní, co se stane, když zadáme očekávanou hierarchii dědičnost a jak se pak změní chování našich tříd.

Dědičnost tříd zadáváme stejně jako dědičnost rozhraní, tj. natažením šipky s trojúhelníkovou hlavičkou od budoucí dceřiné třídy k jejímu budoucímu rodiči. Jediným viditelným rozdílem oproti rozhraním bude to, že natažená šipka nebu-de tentokrát čárkovaná ale plná – viz obr. 8.3.

Správně by měla být použita plná šipka i pro dědičnost rozhraní, pro-tože čárkovaná šipka označuje implementaci. Autoři se však rozhodlipro to, že všechny šipky vedoucí k rozhraní budou čárkované a my je-jich rozhodnutí nemůžeme ovlivnit.

Obrázek 8.3 Definice dědících tříd

Page 323: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 323

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 323 z 433

Poté, co definujete dědičnost, bude malou chvíli jedno, kterou z testo-vacích tříd použijete. V obou jsou definovány stejné metody. Třídy senavzájem liší pouze tím, že třída BezDědičnosti byla upravena tak, aby jí byl překladač ochoten přeložit i tehdy, když ještě nebude zavedenadědičnost. Při pokusu o předčasný překlad třídy SDědičností, tj. o pře-klad před zavedením dědičnosti, by se překladač vzbouřil.

I ve zdrojovém kódu se dědičnost tříd zapisuje stejně jako dědičnost rozhraní, tj. prostřednictvím klíčového slova extends následovaného názvem rodičovské třídy. Jedinou výjimkou je případ, kdy rodičovskou třídou je třída Object, jejíž rodičov-ství explicitně uvádět nemusíme.

Nemusíme, ale můžeme. Přidáte-li do hlavičky kterékoliv třídy, která nemá uvedeného rodiče, klauzuli extends Object, překladač vám třídu bez problému přeloží. Zkuste např. přidat příslušnou klauzuli do hlavičky pokusné třídy a po-žádejte o její překlad. Překladač by měl uposlechnout bez námitek.

Natáhněte nyní mezi třídami Matka, Dcera a Vnučka příslušné dědické šipky a podívejte se, jak se jejich hlavičky změnily. Nyní by měly mít tvar:

public class Matka public class Dcera extends Matka public class Vnučka extends Dcera

Záhlaví třídy Matka se nezměnilo, protože rodiče se v objektovém programování o své děti nestarají1. Veřejně označovat dědictví musí ve svém záhlaví pouze děti. To však znáte už od rozhraní, takže by vás to nemělo překvapit.

Prozatím jsme dědictví tříd zadávali úplně stejně jako dědictví rozhraní. V jedné věci se však dědičnost tříd o dědičnosti rozhraní zásadně liší. Označíme-li rodiče deklarovaného v hlavičce třídy jako vlastního rodiče třídy, pak musí vždy platit:

Třída musí mít vždy PRÁVĚ JEDNOHO vlastního rodiče!

Jedinou třídou, která nemá rodiče, je třída Object. Ta je však zapuštěná hluboko do systému, takže může být trochu nestandardní. Pro všechny ostatní třídy výše uvedené pravidlo platí.

BlueJ ani překladač vám nedovolí zadat pro třídu více než jednoho vlastního rodiče. Jakmile byste chtěli natáhnout od nějakého potomka druhou dědickou šipku, BlueJ by to nepochopil jako přidání rodiče, ale jako změnu rodiče – starý rodič by byl zapomenut a nastavil by se nový.

1 Jak jsme si říkali před chvílí, informaci o dědictví bychom sice mohli teoreticky přidat i do

záhlaví třídy Matka – dědí přeci od třídy Object. V praxi se to však nedělá, takže i my ne-cháme záhlaví v původním tvaru.

Page 324: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

324 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 324 z 433

Budete-li chtít BlueJ oblafnout a místo tahání šipek zapíšete ve zdrojovém kó-du do hlavičky třídy dědění od několika rodičovských tříd tak, jak jsme to mohli udělat v případě rozhraní, odmítne překladač takovouto třídu přeložit.

Nyní se nebudu odvolávat na naši analogii s továrnami na robotí vozi-dla, ale na souborový systém ve vašem počítači. S dědičností je to totiž stejné jako se složkami na disku. Každá podsložka může být pouze vjediné rodičovské složce, ale sama může mít libovolný počet svých podsložek. Obdobně je to i s rodiči: každá třída má právě jednoho rodi-če, avšak může mít libovolný počet potomků.

Podobjekt rodičovské třídy Každý objekt dceřiné třídy v sobě obsahuje podobjekt své rodičovské třídy se všemi jeho metodami a atributy. Instance dceřiných tříd tak dědí atributy svých rodičovských tříd, a to i tehdy, jsou-li tyto atributy soukromé a proto pro dceřinou třídu a její instance nepřístupné.

Soukromé atributy podobjektu rodičovské třídy jsou však nepřístupné pouze po dceřiný objekt. Podobjekt rodičovské třídy je samozřejmě využívat může – jsou přece jeho, resp. jeho třídy.

Pravidlo o podobjektu můžeme aplikovat rekurzivně. Má-li rodičovská třída svého rodiče (ten bude prarodičem její dceřiné třídy), bude tento podobjekt rodi-čovské třídy obsahovat svůj podobjekt své rodičovské třídy, která je prarodičem vlastní třídy původní instance. Tak to pokračuje dále až k pramáti všech tříd, kte-rou je třída Object (viz obr. 8.4).

Obrázek 8.4 Podobjekty v objektech dceřiných tříd

Každá instance má v sobě postupně zanořené podobjekty všech svých rodičů až k objektu třídy Object. Při vytváření objektu dceřiné třídy se vytvoří nejprve podob-jekt rodičovské třídy a teprve pak se kolem něj začne budovat objekt dceřiné třídy. Rodičovský podobjekt se vytváří stejně jako všechny objekty, takže má-li jeho tří-da rodiče, vytvoří se nejprve podobjekt tohoto rodiče atd. atd.

Page 325: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 325

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 325 z 433

Výsledkem je to, že při vytváření každého objektu se nejprve vytvoří instance třídy Object, k ní se přidají rozšiřující atributy a metody a vytvoří se objekt po-tomka prvního řádu, k němu se přidají případné další atributy a metody a vznik-ne potomek druhého řádu a tak dále, až konečně vznikne požadovaná instance.

Budu-li se potřebovat v dalším textu odvolat na konkrétní podobjektinstance, budu jej označovat jako Xxx-podobjekt, kde za Xxx dosadím název vlastní třídy příslušného podobjektu.

Instance třídy vnučka by tedy měla podobjekty Dcera-podobjekt, Mat-ka-podobjekt a Object-podobjekt.

Pokud bychom se zase vrátili k naši analogii s vozidly, tak bychom mohli instance třídy Object prohlásit za tahače, za které se připojují pří-věsy. V celém robotím městě je jediná továrna na tahače – tu představu-je třída object. Všechna auta proto používají stejný typ tahače – instanci třídy Object. Každá ze zbylých továren pak k něm připojí svůj přívěs.

Továrna, která odpovídá třídě, jež není přímým potomkem třídyObject, připojuje své přívěsy na konec soupravy, kterou dostává z to-várny odpovídající příslušné rodičovské třídě. Aby však mohla připojit na konec svůj přívěs, musí nejprve obdržet od rodičovské třídy přísluš-nou soupravu – budoucí podobjekt své instance.

Vše si předvedeme na našem pokusném projektu. Přeložte všechny třídy a požá-dejte některou z testovacích tříd, aby naplnila z přípravku zásobník odkazů. Po provedení operace by mělo okno terminálu vypadat jako na obr. 8.5.

Obrázek 8.5 Okno standardního výstupu po vytvoření matky, dcery a vnučky

Page 326: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

326 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 326 z 433

Výpis přesně demonstruje to, co jsme si říkali před chvílí. Testovací metoda nej-prve vytvářela instanci třídy Matka – a konstruktor o tom poslal na standardní vý-stup zprávu.

Druhým příkazem se vytváří instance třídy Dcera. Dcera je potomek matky, takže se musí nejprve vytvořit podobjekt matky (bude to již druhá matka v pořa-dí) a teprve po ní se dostane ke slovu konstruktor dcery, aby dokončil vytvoření příslušné instance.

Třetí příkaz vytvářející vnučku nám vypíše dokonce tři řádky. Vnučka je po-tomkem dcery, musí se proto nejprve vytvořit dcera. Dcera je ale potomkem mat-ky, takže nejprve nechá vytvořit matku (v pořadí již třetí) a až pak vytvoří dceru (druhou v pořadí). Teprve poté, co je dokončeno vytvoření podobjektu dcery, se dostane ke slovu konstruktor vnučky a dokončí vytvoření první vnučky.

Správně bych měl v předchozích odstavcích hovořit ještě o tom, že třídaMatka je potomkem třídy Object, takže se před vytvořením matky vy-tvoří nejprve podobjekt třídy Object. Jeho konstruktor však o svém pů-sobení žádnou zprávu nenechává, proto se o něm ze standardníhovýstupu nic nedozvíme. Můžeme se o něm pouze dohadovat na zákla-dě toho, jak se chovají ostatní potomci vůči svým rodičům.

Pokud chcete celý proces pozorovat krůček za krůčkem, vložte zarážkuna první příkazy všech konstruktorů a projděte si vytvoření instancetřídy Vnučka krok za krokem.

Podívejte se nyní do útrob jednotlivých instancí na jejich atributy. Zjistíte, že (podle očekávání):

objekt matka má atribut m_pořadí a v něm je hodnota 1,

objekt dcera má atributy d_pořadí s hodnotu 1 a atribut m_pořadí s hodnotou 2 – to je atribut jeho podobjektu rodičovské třídy.

objekt vnučka má atributy v_pořadí s hodnotou 1, d_pořadí (atribut podobjek-tu) s hodnotu 2 a m_pořadí (atribut podpodobjektu) s hodnotou 3,

třída Matka má atribut m_počet a v něm je hodnota 3, protože od této třídy jsou již vytvořeny tří instance – jedna jako samostatný objekt a dvě jako podobjek-ty instancí dceřiných tříd,

třída Dcera má atributy d_počet s hodnotu 2 (jedna samostatná instance a jed-na instance jako podobjekt vnučky) a zděděný atribut m_počet s hodnotou 3,

Page 327: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 327

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 327 z 433

třída Vnučka má atributy v_počet s hodnotou 1 a zděděné atributy d_počet s hodnotu 2 a m_počet s hodnotou 3.

To vše nám ukáže prohlížeč tříd. Neprozradí nám však to, které atributy jsou pro koho dostupné. Všechny atributy našich tříd jsou deklarovány jako soukromé. Z toho vyplývá, že s nimi mohou pracovat pouze konstruktory a metody definova-né ve stejné třídě. Pro ty ostatní jsou nepřístupné.

Objekty tedy příslušné atributy zdědí, ale se žádným z nich nemohou nic dě-lat (tj. nemohou z něj číst ani do něj zapisovat), protože o něm oficiálně prostě ne-vědí. Pracovat mohou pouze s těmi rodičovskými atributy, které nejsou soukromé.

Explicitní volání konstruktoru předka Již tedy víme, že každá instance dceřiné třídy (a to jsou vlastně všechny) v sobě obsahuje podobjekt své rodičovské třídy. Tento podobjekt je plnohodnotným ob-jektem který musí být vytvořen rodičovským konstruktorem, jenž je volán dceři-ným konstruktorem před tím, než dceřiný konstruktor začne se svojí prací na konstrukci dceřiného objektu.

Jako obyčejně i tady dává Java programátorovi na výběr ze dvou možnosti: v automatické verzi zařídí volání konstruktoru rodičovského podobjektu překladač, v „ruční“ verzi je musí zabezpečit programátor.

V automatické verzi vloží překladač před začátek dceřiného konstruktoru skryté volání bezparametrického konstruktoru rodičovské třídy, v ruční verzi si můžeme vybrat, který z rodičovských konstruktorů použít, ale jeho volání musíte explicitně zapsat.

Aby mohl dceřiný konstruktor volat konstruktor svého podobjektu, musí na něj nejprve vidět. Rozhodne-li se rodičovská třída z nejrůznějších důvodů defino-vat bezparametrický konstruktor jako soukromý nebo jej vůbec nedefinovat, ne-zbude vám v dceřiné třídě než volat rodičovský konstruktor „ručně“.

Zkuste si to. Otevřete zdrojový kód třídy Matka a opravte modifikátor bezpa-rametrického konstruktoru na private. Požádáte-li nyní o přeložení třídy Dcera, překladač se vzbouří a oznámí vám:

Matka(Java.lang.String) in Matka cannot be applied to ()

Zpráva nám říká, že se pokoušíme použít bezparametrický konstruktor (prázdné závorky na konci zprávy), zatímco ve třídě je k dispozici pouze jednoparametric-ký konstruktor jehož parametrem je textový řetězec.

Chyby se můžete zbavit např. tak, že ve třídě Dcera trochu změníte definici bezparametrického konstruktoru. Zakomentujte jeho dosavadní tělo (vyberte blok

Page 328: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

328 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 328 z 433

zasahující do obou řádků a stiskněte F8) a nechte jej, aby místo sebe vyvolal jed-noparametrický konstruktor, kterému předá prázdný řetězec: 1 2 3 4 5 6 7

public Dcera() { this( "" ); // System.out.println( "Vytvářím " + d_pořadí + // ". instanci třídy Dcera " ); }

Tím převedete veškerou zodpovědnost za správné zkonstruování instance (a za to, že bude volat dosažitelný rodičovský konstruktor) na druhý konstruktor, který je definován následovně: 1 2 3 4 5 6

public Dcera( String s ) { super( "- pro dceru " + s ); System.out.println( "Vytvářím " + d_pořadí + ". instanci třídy Dcera " + s ); }

Všimněte si prvního příkazu s klíčovým slovem super. Ten volá rodičovský kon-struktor, kterému předává řetězcový parametr. Poté, co rodičovský konstruktor dokončí svoji činnost, vypíše na standardní výstup svoji zprávu.

Pro volání rodičovského konstruktoru pomocí klíčového slova super platí naprosto stejná pravidla jako pro volání konstruktoru pomocí this. I toto volání musí být úplně prvním voláním v těle konstruktoru, před které smíte vložit pouze mezeru nebo komentář.

Protože jak super tak this trvají na tom, aby byly prvními příkazy v těle konstruktoru, je jasné, že lze použít pouze jeden z nich. Při použití thisnemusíte volat super, protože to za vás zařídí konstruktor, kterémupomocí this předáváte řízení.

Badatelé určitě přijdou na to, že existuje způsob, jak zařídit, aby se předzavoláním rodičovského konstruktoru ještě provedla nějaká akce, ale jávám jej neprozradím, protože to není korektní postup.

Necháte-li nyní naplnit zásobník odkazů, bude se pro dceru spouštět jednopara-metrický konstruktor, který na konec řádku ještě připíše, že pracuje pro dceru.

Page 329: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 329

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 329 z 433

Restartujte nyní virtuální stroj (potřebujeme ve třídách vynulovat počitadla instancí) a spusťte test parametrických konstruktorů. V okně terminálu by se měl objevit následující text: Vytvářím 1. instanci třídy Matka Vytvářím 2. instanci třídy Matka - pro dceru Vytvářím 1. instanci třídy Dcera Vytvářím 3. instanci třídy Matka - pro dceru Vytvářím 2. instanci třídy Dcera Vytvářím 1. instanci třídy Vnučka Vytvářím 4. instanci třídy Matka MATKA Vytvářím 5. instanci třídy Matka - pro dceru DCERA Vytvářím 3. instanci třídy Dcera DCERA Vytvářím 6. instanci třídy Matka - pro dceru - pro vnučku VNUČKA Vytvářím 4. instanci třídy Dcera - pro vnučku VNUČKA Vytvářím 2. instanci třídy Vnučka VNUČKA

V prvních třech sadách pracoval přípravek (stejný výstup jste viděli před chvílí), druhá sada je pak výsledkem toho, že testovací metoda vytvořila od každé třídy po jedné instanci za použití jednoparametrických konstruktorů přičemž konstruk-toru vytvářené instance předala text obsahující název třídy zapsaný samými vel-kými písmeny (jestli vás zajímá, prohlédněte si zdrojový kód).

Připadají-li vám výpisy podivné, opět vám doporučuji nastavit dovšech konstruktorů zarážky, nechat vytvořit instanci vnučky a na po-čátku těla konstruktoru se pokaždé podívat, jak vypadá hodnota para-metru.

Chráněné atributy – modifikátor přístupu protected Často bychom potřebovali, aby potomci měli přístup k některým rodičovským atributům a metodám, ke kterým ale „zbytek světa“ pustit nechceme. Java pro tento účel definuje speciální modifikátor přístupu protected (chráněný), který označuje atributy a metody, k nimž budou mít kromě ostatních metod dané třídy přístup i metody jejích potomků.

Vyzkoušejme jeho použití na naší třídě a změňme modifikátor přístupu k bezparametrickému konstruktoru třídy Matka na protected. Vraťte pak bezpara-metrický konstruktor třídy Dcera do původního stavu, tj. nastavte bezparametric-ký konstruktor matky jako veřejný, „odkomentujte“ zakomentované příkazy (vyberte je do bloku a stiskněte F7) a smažte první příkaz volající jednoparamet-

Page 330: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

330 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 330 z 433

rický konstruktor. Pak vše znovu přeložte. Jak se můžete přesvědčit, jde to bez problémů.

Bohužel vám nemohu ukázat, jak se někdo jiný nemůže k chráněnémuatributu probojovat, protože modifikátor protected zpřístupňuje daný atribut nejenom potomkům, ale také třídám v nejbližším okolí, což jsouv případě BlueJ všechny třídy ve stejném projektu. K tomuto tématu seproto vrátím v kapitole Budete si to přát zabalit? na straně 376, kde si ukážeme, jak je možné rozdělit celou úlohu do několika „projektů“ akde se pak tento modifikátor uplatní.

Dědičnost a metody tříd Konstrukci dceřiných tříd jsme tedy zvládli. Nyní nás čekají hrátky s metodami. Pochopit chování metod ve všech souvislostech patří k tomu nejtěžšímu, co vás v této učebnici potká. Pusťte se proto do jeho studia odpočatí s čerstvou hlavou.

U rozhraní jsme to měli jednoduché: tam prostě metoda byla nebo nebyla. O detaily implementace jsme se nestarali, takže dědičnost spočívala pouze v rozší-řen portfolia vyžadovaných metod.

U tříd to bude složitější, protože tady se jedná nejenom o tom, jestli třída bu-de umět na danou zprávu zareagovat (tj. jestli má potřebnou metodu), ale také o tom, jak bude reagovat.

U statických metod je to ještě poměrně prosté: metoda je vždy kvalifikována třídou, takže bude-li v několika „příbuzných“ třídách stejná metoda, bude v kaž-dém okamžiku jasné, která je volána.

Na vyzkoušení jsem vám připravil test, jenž volá metodu zprávy(), která je v každé třídě definovaná. Její tělo jsem však zakomentoval protože po jeho odko-mentování se diagram tříd zbytečně „zašipkuje“. Chcete-li si test vyzkoušet, od-komentujte těla všech tří metod, třídy přeložte a spusťte test.

Jak ukazuje následující výpis, tato metoda volá nejprve statickou metodu zpráva(String) pro svoji instanci a poté volá tyto metody kvalifikované názvy jednotlivých tříd. Statická metody zpráva(String) je přitom definována pouze ve třídách Matka a Dcera. Vnučka musí použít verzi zděděnou od dcery. 1 2 3 4 5 6

public class Dcera extends Matka { public static void zpráva( String text ) { System.out.println( text + " (D)" ); }

Page 331: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 331

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 331 z 433

7 8 9

10 11 12 13 14 15 16 17

public void zprávy() { zpráva( "\nDcera - moje zpráva" ); Matka .zpráva( "- Zpráva matky" ); Dcera .zpráva( "- Zpráva dcery" ); Vnučka.zpráva( "- Zpráva vnučky" ); } //... Deklaraco atributů, konstruktorů a zbylých metod }

Test po svém vyvolání vypíše na standardní výstup následující zprávy: Matka - moje zpráva (M) - Zpráva matky (M) - Zpráva dcery (D) - Zpráva vnučky (D) Dcera - moje zpráva (D) - Zpráva matky (M) - Zpráva dcery (D) - Zpráva vnučky (D) Vnučka - moje zpráva (D) - Zpráva matky (M) - Zpráva dcery (D) - Zpráva vnučky (D)

Všimněte si, že vnučka, která vlastní verzi metody zpráva(String) definovanou nemá, si půjčuje potřebnou metodu od své rodičovské třídy (Metoda vypisuje za text do závorek počáteční písmeno názvu své třídy.)

I když je rozpoznání požadované statické metody jednoduché, přesto se nedoporučuje definovat v příbuzných třídách stejně pojmenované sta-tické metody, protože to, která se má zavolat, bývá občas jasné pouzepřekladači a virtuálnímu stroji, avšak jejich názor se může od názoru programátora lišit. Programátor totiž často podlehne svým falešnýmpředstavám. Proto je lepší prostor pro chyby minimalizovat a pokud možno pojmenovávat metody různě.

Metody instancí, jejich dědění a překrývaní Metody instancí dceřiných tříd můžeme rozdělit do tří skupin:

metody, které třída nově definovala,

Page 332: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

332 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 332 z 433

metody, které třída zdědila a používá je v té podobě, v jaké byly definovány v příslušné rodičovské třídě,

metody, které třída sice zdědila, ale protože jí nevyhovovaly, definovala jejich vlastní verze.

Na každou z nich se nyní na chvilku podíváme.

Nové metody S metodami, které jsou v dané třídě nově definované byste neměli mít problémy, protože ty jste od počátku knihy definovali a doufám, že už jste se s nimi naučili pracovat.

Nepřekryté zděděné metody Zděděné metody mají jednu zvláštnost: nejsou to vlastně metody dané instance, ale metody jejího podobjektu. Když tedy pošlete instanci zprávu, na níž musí rea-govat některá ze zděděných metoda, instance zjistí, že pro danou zprávu nemá vlastní metodu a předá ji svému podobjektu, aby vše zařídil. Navenek se však tváři, že to všechno zařídila ona. To, že správnou reakci zprávu ve skutečnosti zprostředkoval její podobjekt, je stejně interní záležitost, jako to, jak to udělal.

Obrátíme-li se opět k naší analogii, můžete si představit, že v továrně mají samozřejmě nejraději ten vagónek, který na konec vláčku přidali, aproto zařídí, že všechny telefony vedou právě sem. Tady se pak rozho-duje, jestli reakci na zaslanou zprávu zvládne některý z robotů v osádce posledního vozu (tj. metoda definovaná v dané třídě), nebo jestli budoumuset požádat předchozí vagón, aby na zprávu zareagoval.

Překryté zděděné metody Když třídě podoba některé zděděné metody nevyhovuje, může si definovat vlast-ní verzi této metody. Tato nová verze pak pro instance této třídy a jejích potomků překryje onu nevyhovující definici v rodičovské třídě. Kdykoliv proto instanci někdo pošle zprávu vyvolávající tuto metody, instance vždy použije svoji verzi metody a nijak se nepídí po tom, je-li v rodičovské třídě definována stejná (pře-krytá) metoda.

Svoji verzi překryté metody použije instance i v případě, kdy je tato metoda volána z některé z metod rodičovské třídy. Je úplně jedno, že v době, kdy jsme de-finovali rodičovskou třídu, daná dceřiná třída ještě neexistovala. Jakmile v dceřiné

Page 333: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 333

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 333 z 433

třídě některou z metod rodičovské třídy překryjeme, začnou ji instance dceřiných tříd používat všude, tj. i v metodách, které pouze zdědily.

Pro jistotu bych jenom zopakoval: původní rodičovskou verzi metody budeme označovat jako překrytou a novou verzi definované v dceřiné třída budeme označovat jako překrývající.

V naší analogii bychom si to mohli představit tak, že roboti (=metody), kteří mají reakci na zaslanou zprávu na starosti, vykonávají předepsa-nou posloupnost příkazů. Když je tam příkaz, že mají své instanci za-slat nějakou zprávu, nedrcnou do souseda, který ji má na starosti, aleopravdu své instanci pošlou danou zprávu a instance pověří jejím zpra-cováním příslušného robota.

Když ale robot podobjektu pošle své instanci zprávu, pro kterou mápřipraveného vlastního robota-metodu, pověří instance reakcí vlastního (překrývajícího) robota, který zareaguje tak, jak má předepsáno.

Překrytí se však neuplatní u soukromých metod třídy. Soukromou metodu nemů-že dceřiná třída překrýt, protože o ní vůbec neví. Definuje-li proto dceřiná třída stejnou metodu, jako je některá ze soukromých metod rodičovské třídy, je tato metoda považována za zcela novou metodu, která se stejnojmennou soukromou metodou rodičovské třídy vůbec nesouvisí.

V naší analogii bychom mohli říci, že soukromé metody jsou vlastnětajné, tak se nesmějí volat normálním způsobem. Narazí-li proto robot ve svém předpisu na příkaz, aby zaslal tajnou zprávu (=zprávu, na nížreaguje soukromá metoda), použije pro zaslání zprávy své instanci in-terní telefon. Proto zpráva nikdy neopustí vagón a nehrozí, že při při-pojení dalšího vagónu dorazí nejprve tam.

Page 334: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

334 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 334 z 433

Pravidla chování překrytých metod jsou natolik důležitá, že vám je pro jistotu zopakuji ještě jednou:

Metody, které jsou pro potomka viditelné, může potomek překrýtvlastní definicí, které budou jeho instance vždy dávat přednostpřed původní zděděnou verzí.

Nová, dceřiná verze metody se použije ve všech metodách danéinstance, tedy i v nepřekrytých zděděných metodách, a to přesto, že v době definice těchto metod překrývající verze metody vůbec neexistovala.

Soukromé metody nemohou potomci překrýt, protože o nich ne-vědí. Když tedy potomek definuje stejnou metodu (tj. stejně po-jmenovanou a se stejnou sadou parametrů), jako je některá zesoukromých metod rodičovské třídy, je tato metoda považována zazcela novou metodou, která se svojí jmenovkyní nemá nic společ-ného.

Test chování překrývajících a překrytých metod Zase si vše vyzkoušíme v našem pokusném projektu. Otevřete si nejprve zdrojové kódy tříd a podívejte se na definice metod, které se jmenují stejně jako třída (sa-mozřejmě s výjimkou velikosti počátečního písmene). Definice všech tří metod jsou téměř stejné – např. metoda matka() je definována následovně: 1 2 3 4 5 6 7 8

public void matka() { zpráva( "\nMetoda matka() instance " + this ); System.out.println( " Název podobjektu: " + název ); soukromá(); veřejná(); System.out.println( název + ".matka - konec"); }

Metoda identifikuje instanci, které byla poslána příslušná zpráva, a přidá název podobjektu, jehož metoda se právě provádí. Pak zavolá soukromou metodu soukromá() a veřejnou metodu veřejná() a nakonec vypíše zprávu o svém ukon-čení.

Jako identifikaci instance přitom použije text vrácený metodou toString(), o níž víme, že ji překladač automaticky zavolá ve chvíli, kdy narazí na odkaz na in-stanci při operaci slučování textových řetězců. Protože ani jedna z tříd nedefinuje

Page 335: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 335

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 335 z 433

vlastní verzi této metody, bude použita verze zděděná od třídy Object. Připomí-nám, že tato verze vrátí řetězec, v němž je uveden název vlastní třídy instance ná-sledovaný znakem @ a hexadecimálním číslem, které lze považovat za adresu instance.

Metody soukromá() a veřejná() jsou také téměř shodné, a to nejen mezi tří-dami, ale i navzájem. Vypíší na standardní výstup název třídy, v níž jsou defino-vány, a název sebe sama. Pak identifikují podobjekt, do jehož portfolia patří, a instanci, které byla zpráva předána. 1 2 3 4 5 6

private void soukromá() { System.out.println(" Třída Matka, metoda soukromá(): " + "\n Podobjekt: " + název + "\n Instance: " + this ); }

V tabulce 8.1 najdete výstup dvou textů. Test MatkaDceraVnučka zobrazený v levém sloupci volá pro každou instanci metodu pojmenovanou stejně jako její vlastní třída. Při těchto voláních se vliv překrytí neuplatní.

Naproti tomu v pravém sloupci je výsledek testu MatkaMatkaMatka, který volá pro každou instanci metodu matka(). Volání metody pro instanci Matka je ještě to-tožné s testem vlevo, ale u volání této metody pro instance tříd Dcera a Vnučka se začne uplatňovat překrývání metod.

Budete-li chtít vyzkoušet oba testy na svém počítači a porovnat výsledky, po-stupujte následovně:

1. Restartujte virtuální stroj (budou se vám pak lépe počítat instance) a spusťte test MatkaDceraVnučka, který pro každý objekt spustí metodu pojmenovanou stejně jako jeho třída.

Page 336: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

336 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 336 z 433

Tabulka 8.1: Porovnání výstupu překrytých a nepřekrytých metod

Test: MatkaDceraVnučka Test: MatkaMatkaMatka

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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

Vytvářím 1. instanci třídy Matka Vytvářím 2. instanci třídy Matka Vytvářím 1. instanci třídy Dcera Vytvářím 3. instanci třídy Matka Vytvářím 2. instanci třídy Dcera Vytvářím 1. instanci třídy Vnučka Instance matka (M) Metoda matka() instance Matka@ac6a45 Název podobjektu: Matka_1 Třída Matka, metoda soukromá(): Podobjekt: Matka_1 Instance: Matka@ac6a45 Třída Matka, metoda veřejná(): Podobjekt: Matka_1 Instance: Matka@ac6a45 Matka_1.matka - konec Instance dcera (D) Metoda dcera() instance Dcera@175078b Název podobjektu: Dcera_1 Třída Dcera, metoda soukromá(): Podobjekt: Dcera_1 Instance: Dcera@175078b Třída Dcera, metoda veřejná(): Podobjekt: Dcera_1 Instance: Dcera@175078b Dcera_1.dcera - konec Instance vnučka (D) Metoda vnučka() instance Vnučka@42552c Název podobjektu: Vnučka_1 Třída Vnučka, metoda soukromá(): Podobjekt: Vnučka_1 Instance: Vnučka@42552c Třída Vnučka, metoda veřejná(): Podobjekt: Vnučka_1 Instance: Vnučka@42552c Vnučka_1.vnučka - konec

Vytvářím 1. instanci třídy Matka Vytvářím 2. instanci třídy Matka Vytvářím 1. instanci třídy Dcera Vytvářím 3. instanci třídy Matka Vytvářím 2. instanci třídy Dcera Vytvářím 1. instanci třídy Vnučka Instance matka (M) Metoda matka() instance Matka@ac6a45 Název podobjektu: Matka_1 Třída Matka, metoda soukromá(): Podobjekt: Matka_1 Instance: Matka@ac6a45 Třída Matka, metoda veřejná(): Podobjekt: Matka_1 Instance: Matka@ac6a45 Matka_1.matka - konec Instance dcera (D) Metoda matka() instance Dcera@175078b Název podobjektu: Matka_2 Třída Matka, metoda soukromá(): Podobjekt: Matka_2 Instance: Dcera@175078b Třída Dcera, metoda veřejná(): Podobjekt: Dcera_1 Instance: Dcera@175078b Matka_2.matka - konec Instance vnučka (D) Metoda matka() instance Vnučka@42552c Název podobjektu: Matka_3 Třída Matka, metoda soukromá(): Podobjekt: Matka_3 Instance: Vnučka@42552c Třída Vnučka, metoda veřejná(): Podobjekt: Vnučka_1 Instance: Vnučka@42552c Matka_3.matka – konec

2. Otevřete okno terminálu a příkazem Nastavení → Uložit do souboru uložte jeho ob-sah do textového souboru, který nazvěte podle testu MatkaDceraVnučka.txt.

3. Znovu restartujte virtuální stroj, spusťte test MatkaMatkaMatka, který pro každý objekt spustí metodu matka(). Obsah okna terminálu pak uložte do souboru nazvaného (překvapení!) MatkaMatkaMatka.txt.

Page 337: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 337

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 337 z 433

4. Otevřete oba soubory v editoru a umístěte jejich okna vedle sebe tak, abyste je mohli porovnávat jako já v tabulce.

5. K tomu si ještě otevřete některý ze zdrojových kódů, abyste mohli sledovat, kudy program v daném okamžiku prochází.

Porovnání Pojďme se nyní podívat, v čem se oba výstupy shodují, v čem se liší a proč tomu tak je.

Možná vás napadlo, že by bylo nejlepší prostě program odkrokovat azjistit, jak funguje. To bych vám asi v tuto chvíli neradil, protože bysteasi v programu brzy zabloudili. Obrňte se trpělivostí a pojďte s námiprojít tabulku a související zdrojové kódy nejprve očima. Až vám začnebýt jasné, co má program v jednotlivých okamžicích dělat, můžete jejpak začít krokovat a utvrdit se v tom, že pracuje přesně podle předpo-kladů.

Až do řádku 23 jsou oba výstupy totožné. Aby také ne, když v obou případech děláme totéž: vytvoříme přípravkem instance a zavoláme metodu matka() instan-ce matka().

V řádku 24 najdeme prvou odchylku. Z textu řádku vyčteme, že v levém sloupečku voláme metodu dcera.dcera, kdežto v pravém dcera.matka(). To ale víme, zajímavé to proto začne být až od řádku následujícího.

Podobjekt Zde se totiž dozvídáme, že ve skutečnosti voláme metodu podobjektu Matka_2. Došlo na to, o čem jsme si před chvílí povídali. Dceři byla poslána zpráva, pro kterou neměla připravenou vlastní metodu, a proto předala zprávu svému Mat-ka-podobjektu, aby na ni zareagoval (jak si můžeme přečíst v 3. řádku, Mat-ka-podobjektem první dcery je druhá matka). Ten, kdo poslal dceři zprávu, si ale bude myslet, že na ni zareagovala dcera, protože nemá šanci zjistit, jak to má dce-ra uvnitř instance zorganizované.

Soukromá metoda Poté, co vypíše informace o tom, s kterými instancemi máme tu čest, zavolá meto-da matka() soukromou metodu soukromá() a ta se nám ne řádku 26 ohlásí. Všim-něte si, že v pravém sloupci byla zavolána metoda definovaná ve třídě Matka na

Page 338: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

338 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 338 z 433

rozdíl od levého sloupce, v němž je volána metoda definovaná ve třídě Dcera. To je ale naprosto správně, protože jsme si přece říkali, že při volání soukromých me-tod se vždy použijí metody z té třídy, ve které se nachází definice volající metody (vzpomeňte si na naši analogii s interním telefonem v rámci vagónu).

Řádky 27 a 28 nám prozradí, že stále pracujeme s podobjektem s názvem Matka_2 a také se stejnou instancí (máme-li se stále pohybovat v jedné třída, tak to ani jinak nejde, že?).

Veřejná metoda Na řádku 29 se dozvídáme, že vstupujeme do metody veřejná(). Na tomto řádku je zajímavé právě to, že je v obou sloupcích stejný, což znamená, že se v obou pří-padech volá stejná definici ve stejné třídě, a to přesto, že v levém sloupci je meto-da veřejná() volána z metody definované ve třídě Dcera a vpravo z metody definované ve třídě Matka. Tentýž příkaz zavolá vlevo metodu ze stejné třídy a vpravo metodu z jiné třídy.

Nepochopitelné je to však pouze pro neznalého. My ale už víme, že v obou případech se posílá ta samá zpráva té samé instanci, takže v obou případech musí dojít k zavolání té samé metody – v našem případě metody veřejná() definované ve třídě Dcera.

Instance vnučka

Opatrně zkusím předpokládat, že jste vše zhruba pochopili, a proto jsem pro vás připravil jednoduchý úkol: vysvětlete obdobným způso-bem shody a rozdíly ve výpisech v levém a pravém sloupci u volání metod pro instanci třídy Vnučka.

Vyvolání překryté verze metody V překrývajících dceřiných metodách často potřebujeme použít překrytou rodi-čovskou verzi definované metody. Velmi častou je např. situace, kdy překrývající metoda nejprve něco připraví a pak zavolá překrývanou rodičovskou verzi nebo naopak zavolá nejprve rodičovskou verzi a pak něco doplní.

V takovýchto případech můžeme použít klíčové slovo super (známe je již od konstruktorů), které bychom mohli považovat za takové this pro rodičovský po-dobjekt. Budeme-li chtít např. překrýt metodu toString() tak, že za text vracený rodičovskou metodou přidáme písmeno D uzavřené v kulatých závorkách, bude její tělo tvořeno příkazem:

Page 339: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 339

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 339 z 433

return super.toString() + "(D)";

Při používání super bychom měli vědět, že:

Použitím super posíláme zprávu rodičovskému podobjektu. Pomocí klíčové-ho slova super proto můžeme volat pouze metody bezprostředního rodiče. Je přitom jedno, jestli jsou tyto metody definovány v rodičovské třídě nebo je třída zdědila.

Použití super není možné zřetězit, tj. není možné použít super.super. Není proto možné volat rodičovskou třídou překryté metody prarodiče.

Volání rodičovských metod pomocí klíčového slova super je konečné a nemá na něj vliv žádné pozdější překrytí jakékoliv metody.

Pro testování vlastností volání rodičovských verzí metod jsem připravil metodu rodiče(), kterou najdete v třídách Dcera a Vnučka, a test, který ji zavolá. V této metodě instance zavolá nejprve „svoji“ a poté rodičovskou verzi metody veřejná() a ve vnuččině verzi zavolá ještě rodičovskou verzi metody rodiče().

1 2 3 4 5 6 7 8 9

public void rodiče() { System.out.println("Vnučka - moje verze metody veřejná():"); veřejná(); System.out.println("Vnučka - rodičovská verze metody veřejná():"); super.veřejná(); System.out.println("\nVnučka - rodičovská verze metody rodiče():\n"); super.rodiče(); }

Spuštěním testu Super získáte v terminálovém okně následující výpis: 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18

Vytvářím 1. instanci třídy Matka Vytvářím 2. instanci třídy Matka Vytvářím 1. instanci třídy Dcera Vytvářím 3. instanci třídy Matka Vytvářím 2. instanci třídy Dcera Vytvářím 1. instanci třídy Vnučka Volám dcera.rodiče Dcera - moje verze metody veřejná(): Třída Dcera, metoda veřejná(): Podobjekt: Dcera_1 Instance: Dcera@175078b Dcera - rodičovská verze metody veřejná(): Třída Matka, metoda veřejná(): Podobjekt: Matka_2

Page 340: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

340 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 340 z 433

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

Instance: Dcera@175078b Volám vnučka.rodiče Vnučka - moje verze metody veřejná(): Třída Vnučka, metoda veřejná(): Podobjekt: Vnučka_1 Instance: Vnučka@42552c Vnučka - rodičovská verze metody veřejná(): Třída Dcera, metoda veřejná(): Podobjekt: Dcera_2 Instance: Vnučka@42552c Vnučka - rodičovská verze metody rodiče(): Dcera - moje verze metody veřejná(): Třída Vnučka, metoda veřejná(): Podobjekt: Vnučka_1 Instance: Vnučka@42552c Dcera - rodičovská verze metody veřejná(): Třída Matka, metoda veřejná(): Podobjekt: Matka_3 Instance: Vnučka@42552c

Předpokládám, že vám nemusím tento výpis podrobně rozebírat a že si v něm po-tvrzení toho, co jsme si před chvílí řekli, najdete sami.

8.3 Vytváříme dceřinou třídu Uff uff uff – bylo toho hodně, co? Nejvyšší čas přestat si hrát a vytvořit něco tro-chu praktičtějšího. Zavřete proto projekt s matkou, dcerou a vnučkou a otevřete projekt 08_Dědičnost_tříd_A, v němž se znovu vrátíme k našim geometrickým tva-rům.

Projekt 08_Dědičnost_tříd_A je výchozím projektem další části této kapitoly.Je to v podstatě projekt, s nímž jsme končili předchozí kapitolu. V jeho třídách jsem provedl jednu drobnou změnu, o níž vám za chvíli povím, a pro zjednodušení jsem z něj odstranil třídy, které jsme v průběhu předchozích kapitol vytvořili a které prozatím nebudeme potřebovat.

Třídy, které v průběhu zbytku kapitoly vytvoříme jakož i vzorovépodoby všech tříd, které budete mít za úkol vytvořit samostatně, najde-te v projektu 08_Dědičnost_tříd_B (nekončí písmenem Z, protože to tento-krát není závěrečný projekt kapitoly).

Page 341: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 341

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 341 z 433

Obrázek 8.6 Výchozí projekt této kapitoly

Jednoduchá dceřiná třída Při úvodním výkladu o rodičovských a dceřiných třídách jsme si jako příklad ob-jektů, pro které je vhodné vytvořit dceřinou třídu, uváděli kruh a čtverec. Pojďme nyní spolu definovat třídu Čtverec.

1. Definujte novou standardní třídu, nazvěte ji Čtverec a otevřete její zdrojový kód.

2. V diagramu tříd přesuňte třídu vpravo vedle třídy Obdélník a natáhněte od ní k obdélníku dědickou šipku.

3. Přesvědčte se, že BlueJ vám opět ušetřil práci s psaním a upravil hlavičku tří-dy do tvaru

public class Čtverec extends Obdélník

Page 342: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

342 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 342 z 433

4. Projděme si zdrojový kód. Atribut třídy počet může čtverec převzít (= zdědit) od obdélníku. Nepotřebujme, aby si čtverec počítal své instance sám a bude nám stačit, když budou počítány spolu s obdélníky. Můžete tedy ve zdrojo-vém kódu třídy Čtverec tento atribut smazat.

5. Obdobně může čtverec převzít od obdélníku i atribut pořadí, takže ve čtverci smažete i ten.

6. Ve zdrojovém kódu se dostáváme ke předpřipravenému prázdnému bezpa-rametrickému konstruktoru. Pokud si nepamatujete, jak pracuje obdélníkův bezparametrický konstruktor, zkuste třídu přeložit a požádejte ji ó vytvoření instance.

7. Pokud jste postupovali přesně tak, jak jsem říkal, měl by se teď překladač vzbouřit, že v metodě toString() používáte proměnnou pořadí, ale nemáte ji definovanou.

8. Dohodneme se, že i v tomto případě se spokojíme se zděděnou verzí metody, takže ji můžete klidně smazat a zkusit vše přeložit znovu. Tentokrát by se to mělo podařit.

9. Nechte si vytvořit instanci a podívejte se jí do útrob. Jak uvidíte, její šířka a výška se liší. Nebude nám, než upravit konstruktor tak, aby se vytvořil ten správný obdélník.

10. Zadejte do těla bezparametrického konstruktoru příkaz: super( 0, 0, AP.getKrok(), AP.getKrok() );

Tady vám prozradím, že jsem pro tuto kapitolu změnil ve všech třídách gra-fických obrazců přístupová práva k jejich statickým atributům AP, v nichž si (stejně jako my ve stromu) uchovávají odkaz na aktivní plátno. Původní private jsem změnil na protected. Budete-li proto vytvářet potomky těchto tříd, můžete tohoto atributu využít.

11. Zkuste nyní opět třídu přeložit a vytvořit její instanci. Pohlédnete-li jí do útrob, už by nám měly hodnoty jejích atributů vyhovovat. Budete-li chtít pádnější důkaz, požádejte o odkaz na aktivní plátno a předejte mu instanci – krásný čtverec, že?

12. Rozbalte nyní seznam metod zděděných z třídy Obdélník. Projděte jej a vyti-pujte metody, které bude nutno překrýt, protože nám jejich původní podoba nebude vyhovovat.

Tak co, které jste vybrali? Já jsem dospěl k závěru, že všechny metody by se nám hodily s výjimkou jediné, a tou je metoda setRozměr(int,int), která umožňuje nastavení různých délek stran a která by nám tak mohla pokazit

Page 343: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 343

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 343 z 433

čtvercovost našeho čtverce.

Hrubou kostru bychom tedy měli hotovou, teď ještě zbývá vymyslet, jak defino-vat překrývající verzi metody setRozměr(int,int). Můžeme si vybrat ze dvou možných reakcí na zadání různých velikostí stran:

„rozčílíme se“ a vyvoláme nějaký chybový stav,

upravíme špatně zadané velikosti na takové, které nám budou vyhovovat, a ty pak nastavíme.

Obecně není možno říci, která reakce je lepší – záleží na řešené úloze. Já jsem pro-to vybral druhou možnost, protože je méně konfliktní.

Při rozhodování o právě velikosti parametrů máme zase řadu možností. Zvo-lil jsem tu, při které se nastaví oba rozměry na menší ze zadaných velikostí. K to-mu využijeme statickou metodu min(int,int), kterou najdeme v systémové třídě Math. Překrývající verze metody setRozměr(int,int) by tedy mohla mít následující tvar: 1 2 3 4 5

public void setRozměr( int šířka, int výška ) { int strana = Math.min( šířka, výška ); super.setRozměr( strana, strana ); }

Doplňte definici této metody a třídu Čtverec vyzkoušejte. Zkuste instanci posou-vat jak sami, tak pomocí přesouvače. Zkuste ji pak měnit její rozměry, a to opět jak ručně, tak pomocí kompresoru.

Definujte třídu Kruh jako potomka třídy Elipsa.

Konstruktory potomka Jedinou věcí, kterou dceřiné třídy nemohou zdědit, jsou konstruktory. Rodičovské konstruktory nemohou použít k ničemu jinému, než ke konstrukci svých podob-jektů. Každý konstruktor, který bude dceřiná třída potřebovat, je proto třeba zno-vu definovat.

Definici dceřiných konstruktorů není vhodné odbýt. Pokud se rozhodneme, že třída bude definovat pouze bezparametrický konstruktor a někdy v budoucnu se rozhodneme definovat její potomky, budou tito potomci odkázáni pouze na něj a nebudou již mít možnost využít parametrické konstruktory svých prarodičů.

Page 344: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

344 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 344 z 433

Při návrhu dceřiné třídy je proto třeba se zamyslet nad tím, které konstrukto-ry není možno vynechat, protože by nám mohly při definici případné dceřiné tří-dy chybět. Vynechat můžeme pouze takové konstruktory, které bychom mohli v případě potřeby bez problému dodefinovat pomocí rodičovských konstruktorů, které budou k dispozici.

V případě našich čtverců a kruhů jsou těmito nutnými konstruktory ty, které umožňují zadat pozici, rozměr i barvu vytvářeného obrazce. Ostatní typy kon-struktory, které nabízí třídy Obdélník a Elipsa a jejichž ekvivalenty by se nám mohly v budoucnu hodit, je možno z těchto základních konstruktorů v případě potřeby odvodit

Doplňte do definic tříd Čtverec a Kruh konstruktory, které umožňují za-dat jejich rozměr, pozici a barvu. Doplňte pak ekvivalenty ostatních konstruktorů nabízených třídami Obdélník a Elipsa.

Složitější dceřiná třída Účelem tříd Čtvrec a Kruh bylo definovat obrazec, který je schopen zabezpečit ně-jakou dodatečnou funkčnost – v našem případě zaručit, aby oba rozměry obrazce byly shodné. To bylo opravdu jednoduché rozšíření a k jeho dosažení nám stačilo překrýt jedinou metodu předka.

Podívejme se nyní na maličko rafinovanějšího potomka, u nějž budeme mu-set k dosažení požadovaných vlastností sáhnout trochu hlouběji do definic jeho metod a na kterém si pak budeme moci znovu ověřit platnost některých zákoni-tostí, o nichž jsme si povídali v prvých dvou podkapitolách této kapitoly.

Definujme třídu XObdélník, jejíž instance budou obdélníky s viditelnými úh-lopříčkami, které zruší pravidlo, že jejich souřadnice budou souřadnicemi levého horního rohu, a budou za svoje souřadnice považovat souřadnice průsečíku svých úhlopříček.

Projdeme si spolu definici této třídy krok za krokem a opět si připomeneme, jak bychom měli v takovýchto případech postupovat.

Příprava prázdné třídy a testovací třídy Začneme tím, že vytvoříme novou standardní třídu a k ní hned vytvoříme pří-slušnou testovací třídu a v ní připravíme základní testy.

V řadě případů je nejrychlejší netancovat kolem přípravku, vytváření instancí a vyvolávání potřebných metod a připravit testy ručně. Pak totiž můžete v testech použít i metody, které jste v testované třídě ještě nedefinovali. Testovací třídu se

Page 345: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 345

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 345 z 433

pak sice někdy nepodaří hned přeložit, ale to nevadí, protože tím máte definova-ný první úkol: definovat potřebně metody, aby se testovací třída přeložila.

Protože vím, že vytváření testů je pracné a vyžaduje jistou představu otom, co má vlastně testovaná třída umět, tak jsem ji pro vás již připravil – najdete ji (dle očekávání) v projektu 08_Dědičnost_tříd_B. Třídu XObdélníkz něj ale nestahujte, tu budeme vytvářet od počátku pěkně krok za kro-kem.

Vytvořte tedy novou standardní třídu a pojmenujte ji XObdélník. Před tím, než budete číst dál, projděte její zdrojový kód a rozmyslete si, které z předpřiprave-ných atributů a metod bude výhodné ponechat a které bude vhodné smazat.

Tak co, vymyšleno? Já bych to udělal naprosto stejně jako se čtvercem a kru-hem – ponechal bych pouze prázdný bezparametrický konstruktor.

Po této úpravě ale nepůjde přeložit testovací třídu, protože ta vyžaduje vedle bezparametrického konstruktoru ještě konstruktory se čtyřmi a pěti parametry. Nadefinujeme prozatím prázdné, aby se testovací třídu podařilo přeložit.

Definice konstruktorů Nejlepší bude začít definicí nejobecnějšího pětiparametrického konstruktoru, pro-tože zbylé dva jej budou stejně volat s nějakými implicitními hodnotami paramet-rů.

Je zřejmé, že tento konstruktor bude začínat voláním rodičovského pětipara-metrického konstruktoru. Rozmyslete si, jaké bude muset zadat tomuto konstruk-toru parametry, když víte, že pro náš konstruktor jsou zadávané souřadnice souřadnicemi středu vytvářeného obrazce, kdežto pro rodičovský konstruktor jsou souřadnicemi jeho levého horního rohu.

Na obdélníku mají být nakresleny jeho úhlopříčky – budeme tedy potřebovat dva atributy, kam uložíme odkazy na příslušné čáry (atributy nejlépe konstantní, protože čáry se již nebudou měnit). Po zavolání rodičovského konstruktoru pak musíme tyto atributy inicializovat.

Zkuste nyní definovat svoji vlastní verzi těla pětiparametrického konstrukto-ru a pak si ji porovnejte s následující vzorovou definicí. 1 2 3 4 5 6

public XObdélník( int x, int y, int šířka, int výška, Barva barva ) { super( x-šířka/2, y-výška/2, šířka, výška, barva ); //Pomocné proměnné mohu bohužel definovat až nyní, //protože před voláním rodičovského kontruktoru nesmí být žádný příkaz int x0 = x - šířka/2;

Page 346: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

346 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 346 z 433

7 8 9

10

int y0 = y - výška/2; hlavní = new Čára( x0, y0, x0+šířka, y0+výška ); vedlejší = new Čára( x0, y0+výška, x0+šířka, y0 ); }

Dohodneme-li se, že implicitní pozice obdélníka bude v levém horním rohu a že jeho implicitní barva bude červená, pak budou jasné i definice zbylých konstruk-torů. Zkuste si je nadefinovat a své definice si porovnejte s následujícími: 1 2 3 4 5 6 7 8 9

public XObdélník() { this( AP.getKrok(), AP.getKrok(), 2*AP.getKrok(), AP.getKrok() ); } public XObdélník( int x, int y, int šířka, int výška ) { this( x, y, šířka, výška, Barva.ČERVENÁ ); }

Konstruktory jsou nadefinovány, můžete požádat testovací třídu, aby naplnila zá-sobník odkazů z přípravku.

Metoda kresli(java.awt.Graphics2D) Zásobník odkazů se sice naplní, ale vykreslení naších obdélníků není dokonalé – chybí jim požadované přeškrtnutí úhlopříčkami. Jak vás asi napadne, potřebuje-me překrýt metodu kresli(java.awt.Graphics2D). Její definice je ale jednoduchá a předpokládám, že vás napadne i bez nahlížení do vzorového řešení: 1 2 3 4 5 6

public void nakresli( java.awt.Graphics2D kr ) { super .nakresli( kr ); hlavní .nakresli( kr ); vedlejší.nakresli( kr ); }

Naplnění zásobníku odkazů by mělo již proběhnout bez problémů. Můžeme tedy spustit první test – zkuste např. test posunů.

Metoda setPozice(int,int) Jestli postupujete krok za krokem se mnou, zaznamenali jste, že se sice posunul podkladový obdélník, avšak neposunuly se příslušné úhlopříčky.

V první etapě by vás možná napadlo překrýt parametrické posunové metody a nechat v nich posunout jako obdélník, tak úhlopříčky podobně, jako jsme je v

Page 347: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 347

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 347 z 433

poslední metodě nechávali nakreslit.Dopředu vám však prozradím, že byste stej-ně za chvíli narazili. Zkrátíme proto naše objevování správného řešení a navedu vás na ně rovnou.

Podíváte-li se do definice třídy Obdélník, zjistíte, že posunové metody jsou naprogramovány téměř stejně, jako jsme je definovali pro náš strom: využívají metody setPozice(int,int). Bude tedy nejlepší upravit ji.

Zkuste to a svoji metodu pak vyzkoušejte spuštěním posunových testů. Kdy-byste měli problémy, můžete si svoje řešení porovnat s následujícím: 1 2 3 4 5 6 7 8

public void setPozice( int x, int y ) { int s2 = getŠířka() / 2; int v2 = getVýška() / 2; super.setPozice( x-s2, y-v2 ); hlavní.setPozice( x-s2, y-v2 ); vedlejší.setPozice( x-s2, y+s2 ); }

Tak co? Předpokládám, že vám vyšel stejný výsledek jako mně: obdélník se místo doprava posunul nahoru (viz obr. 8.7).

Obrázek 8.7 Úprava metody nakresli(java.awt.Graphics2D) ukázala další chybu

Předpokládám, že nemáte představu, čím by to mohlo být. V takových chvílích bývá někdy nejlepší sledovat činnost programu krok za krokem. Otevřete proto zdrojový kód testovací třídy a vložte do něj zarážku do metody testPosuny() na řádek, kde se v volá metoda posunVprav(). Nyní znovu spusťte test posunů:

Page 348: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

348 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 348 z 433

1. Nejprve se objeví dialogové okno, které nám oznámí, že přijde posun vpravo. Stiskněte ANO.

2. Otevře se okno debuggeru a současně zdrojový kód testovací tříd s ukazate-lem na nastavené zarážce. Požádejte o krok dovnitř.

3. Otevře se zdrojový kód třídy Obdélník a ukazatel se nastaví na jediný příkaz těla metody posunVpravo(), kterým je volání její jednoparametrické verze.

Nyní bychom sice mohli požádat znovu o kro dovnitř, ale to bychom nejprve vběhli do metody getKrok() ve třídě AktivníPlátno, z ní bychom se vrátili zpět sem a teprve pak bychom se přesunuli tam, kam nás srdce táhne. Abychom si tento mezikrok ušetřili, využijeme toho, že metoda, kam směřu-jeme, je definována hned vedle. Umístíme do jejího těla zarážku a dáme de-buggeru příkaz Pokračovat.

4. Zastavili jsme se na jediném příkazu těla metody posunVpravo(int), kterým je příkaz

setPozice( xPos+vzdálenost, yPos );

Podívejme se nejprve, jaké mu budeme předávat parametry. V okně debug-geru zjistíme, že xPos=0, vzdálenost=50 a yPos=25. Žádáme tedy, aby nám tato metoda přesunula obdélník na souřadnice [50;25]. Jak ale víme nečiní tak. Budeme proto opět krokovat dovnitř, abychom zjistili, proč tak nečiní.

5. Otevře se znovu okno se zdrojovým kódem třídy XObélník a ukazatel se na-staví na první příkaz metody setPozice(int,int). A už by nám mělo pomalu začít svítat.

Podíváte-li se na hodnoty parametrů, vidíte že je po metodě požadová-no, aby přesunula svůj objekt na pozici [50;25]. Jenomže souřadnice xobdél-níků se počítají vůči jejich středu, takže bychom měli po této metodě požadovat, aby svůj obrazec přesunula na pozici [100,50].

Prohlédnete-li si tělo metody, uvidíte, že tyto už dopředu zmenšené za-dané souřadnice ještě dále zmenšujeme, takže není divu, že se nám výsledný obrazec posouvá jinam než potřebujeme.

Page 349: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 349

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 349 z 433

Jak přesvědčit objekt, aby se pokaždé choval jinak Následující úvaha je natolik důležitá, že jsem si ji dovolil vypíchnout do sa-mostatného rámečku. Doufám, že se mi ji podaří vysvětlit dostatečně prů-zračně.

Potřebujeme vymyslet, jak nevhodné chování xobdélníků vyléčit. Pře-krývající metoda setPozice(int,int) dostává od volající metody evidentně špatné parametry. Musíme se tedy podívat do volající metody, jak to zlepšit.

Ve volající metodě posunVpravo(int) jsme ale již byli a tenkrát jsme byli s podobou předávaných parametrů spokojeni. Jak to, že ve volané metodě snimi spokojeni nejsme? Je to proto, že ve volající metodě jsme si mysleli(špatně), že nastavujeme souřadnice obdélníku a až ve volané metodě jsme si uvědomili, že se vlastně nastavují souřadnice xobdélníku. Potřebovali by-chom tuto informaci předat také „o patro výš“.

Jediný příkaz volající metody chce po volané metodě, aby přemístila ob-jekt o vzdálenost doprava a vypočítává jí požadované souřadnice. Počítá je však z hodnot atributů xPos a yPos, ve kterých jsou uloženy souřadnice levé-ho horního rohu obdélníku.

Kdybychom místo přímého výběru hodnoty souřadnic z těchto atributůpožádali instanci, ať nám své souřadnice prozradí, prozradil by nám obdélník souřadnice svého levého horního rohu kdežto xobdélník by nám vrátil sou-řadnice svého středu.

Když tedy v metodě nahradíme výběr hodnot z atributů xPos a yPos vo-láním metod getX() a getY(), získáme od obdélníku stejná čísla, jako před tím. Pokud pak tyto metody ve třídě XObdélník vhodně překryjeme, začnou nám vracet souřadnice středu xobdélníku, a to je právě to, co potřebujeme.

Při těchto úpravách ale nesmíme zapomenout ani na ostatní metody, kte-ré potřebují pracovat se souřadnicemi a získávají je přím z atributů. S atributy budou od této chvíli pracovat již pouze konstruktory a metody, které jejichhodnoty zjišťují a nastavují. Všichni ostatní začnou místo atributů používatpříslušné přístupové metody, čímž umožní potomkům změnit význam sou-řadnic.

Všechny metody, v nichž bychom tuto úpravu neprovedli, bychom jinakmuseli překrýt. Nahrazení přímého oslovení atributů zavoláním jejich pří-stupové metody nám umožní tyto metody zdědit, aniž bychom se o ně muse-li dále starat.

6. Upravte ve třídě Obdélník v metodě posunVpravo(int) její jediný příkaz do tvaru:

setPozice( getX()+vzdálenost, getY() );

Page 350: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

350 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 350 z 433

7. Definujte ve třídě XObdélník překryté verze metod getX() a getY(): public int getX() { return super.getX() + getŠířka()/2; } public int getY() { return super.getY() + getVýška()/2; }

8. Projděte zdrojový kód třídy Obdélník a upravte ostatní místa, kde je vhodné změnit přímé „oslovení“ atributu voláním příslušné přístupové metody. Na-povím vám, že to bude v metodách getPozice(), getOblast(), posunDolů(int) a toString().

Někteří z vás možná namítnou, že bychom měli upravit také metodu nakresli(java.awt.Graphics2D), která atributy xPos a yPos také používá, ale to bychom neudělali dobře. Tato metoda má správně nakreslit právě obdélník a má jej nakreslit přesně na tom místě, kam patří. Proto jesprávné, že pro zadání jeho pozice používá atributy, které hovoří právěo něm a ne o jeho potomcích.

Z toho samozřejmě zákonitě vyplývá, že kdykoliv budeme v bu-doucnosti od obdélníku odvozovat nějakého potomka, budeme určitě muset tuto metodu upravit, a to i tehdy, pokud instance tohoto potom-ka zůstanou obyčejnými obdélníky, které budou mít pouze jiný vztažný bod, vůči němuž se budou počítat jejich souřadnice.

Tato otázka je daleko důležitější, než na první pohled vypadá, a její zodpovězení má poměrně závažné důsledky. Proto se k ní ještě vrátím.

9. Vyzkoušejte test posunů a ověřte, že vše pracuje tak, jak má.

10. Vyzkoušejte i testy přesouvače.

11. Vyzkoušejte test správné implementace IHýbací, v němž je použit směrova-telný kompresor.

Poslední test nemůže chodit, protože jsme neudělali s nastavováním rozměru totéž, co s nastavováním pozice. Zkuste proto doprovodit tří-du do cíle sami. Kontrolní řešení naleznete v projektu 08_Dědičnost_tříd_B.

Page 351: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 351

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 351 z 433

Samostatná úloha: Terč Abyste si definici rafinovanějších dceřiných tříd procvičili, připravil jsem pro vás ještě jeden příklad.

Definujte třídu Terč, jejíž instance nakreslí na plátno tři soustředné kruhy přeškrtnuté záměrným křížem (viz obr. 8.8). Navíc bude, stejně jako předchozí třída XObdélník, definovat pozici svých instancí jako po-zici společného středu soustředných kruhů a průsečíku os záměrného kříže.

Pro ty, kteří budou mít s úlohou problémy, jsem opět připravil vzo-rové řešení bez dokumentačních komentářů (použité oddělovací ko-mentáře jsem pro lepší orientaci ve zdrojovém kódu nechal). To s komentáři si můžete zkopírovat z projektu 08_Dědičnost_tříd_B.

Obrázek 8.8 Terč

public class Terč extends Kruh { //== KONSTANTNÍ ATRIBUTY TŘÍDY ================================================= private static final Barva B1 = Barva.ŽLUTÁ; private static final Barva B2 = Barva.MODRÁ; private static final Barva B3 = Barva.ČERVENÁ; //== KONSTANTNÍ ATRIBUTY INSTANCÍ ============================================== private final Kruh mezi; private final Kruh střed; private final Čára vodor; private final Čára svislá;

Page 352: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

352 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 352 z 433

//############################################################################## //== KONSTRUKTORY A METODY VRACEJÍCÍ INSTANCE VLASTNÍ TŘÍDY ==================== public Terč() { this( AP.getKrok()/2, AP.getKrok()/2, AP.getKrok() ); } public Terč( int x, int y, int průměr ) { this( x, y, průměr, B1, B2, B3 ); } public Terč( Pozice počátek, int průměr ) { this( počátek.x, počátek.y, průměr ); } public Terč( Pozice počátek, int průměr, Barva barva1, Barva barva2, Barva barva3 ) { this( počátek.x, počátek.y, průměr, barva1, barva2, barva3 ); } public Terč( Oblast oblast ) { this( oblast.x, oblast.y, Math.min(oblast.šířka, oblast.výška) ); } public Terč( Oblast oblast, Barva barva1, Barva barva2, Barva barva3 ) { this( oblast.x, oblast.y, Math.min(oblast.šířka, oblast.výška), barva1, barva2, barva3 ); } public Terč( int x, int y, int průměr, Barva barva1, Barva barva2, Barva barva3 ) { super( x-průměr/2, y-průměr/2, průměr, barva1 ); mezi = new Kruh( 0, 0, 0, barva2 ); střed = new Kruh( 0, 0, 0, barva3 ); vodor = new Čára( 0, 0, 0, 0 ); svislá= new Čára( 0, 0, 0, 0 ); setRozměr( průměr ); } //== PŘÍSTUPOVÉ METODY ATRIBUTŮ INSTANCÍ ======================================= public int getX() { return super.getX() + getŠířka()/2; } public int getY() {

Page 353: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 353

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 353 z 433

return super.getY() + getVýška()/2; } public void setPozice( int x, int y ) { int r = getŠířka() / 2; int s = r / 3; AP.nekresli(); super .setPozice( x-r, y-r ); mezi .setPozice( x-2*s, y-2*s ); střed .setPozice( x-s, y-s ); vodor .setPozice( x-r, y ); svislá.setPozice( x, y-r ); AP.vraťKresli(); } public void setRozměr( int šířka, int výška ) { int průměr = Math.min( šířka, výška ); int p2 = průměr / 2; int p3 = průměr / 3; int x = getY(); int y = getY(); AP.nekresli(); //Nejprve nastavíme všechny rozměry super.setRozměr( průměr, průměr ); mezi .setRozměr( 2*p3 ); střed.setRozměr( p3 ); //A pak všechny hormadně přesuneme setPozice( x, y ); //Osový kříž nestačí přesunout, protože se mohl změnit jeho rozměr vodor .spoj( x-p2, y, x+p2, y ); svislá.spoj( x, y-p2, x, y+p2 ); AP.vraťKresli(); } //== PŘEKRYTÉ KONKRÉTNÍ METODY RODIČOVSKÉ TŘÍDY ================================ public void nakresli( java.awt.Graphics2D kr ) { super .nakresli( kr ); mezi .nakresli( kr ); střed .nakresli( kr ); vodor .nakresli( kr ); svislá.nakresli( kr ); } }//public class Terč

Page 354: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

354 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 354 z 433

8.4 Vytváříme rodičovskou třídu Na příkladu čtverce a kruhu jste názorně viděli jeden z velmi oceňovaných příno-sů dědičnosti, kterým je odstranění nutnosti zdvojeného psaní stejného kódu. Čtverec a kruh většinu metod, které zdědily, ponechaly beze změny, protože jim jejich funkčnost vyhovovala, takže jste pro ně nemuseli definovat jejich vlastní verze metod a ušetřili jste si tak spoustu psaní.

Tady ale nejde pouze o to, jestli ušetříte nějaké to ťuknutí do klávesnice, ale především o to, že kód metod, které jsou pro obě třídy stejné, je soustředěn na jednom místě, takže když se později rozhodnete jej z nejrůznějších důvodů změ-nit, nemusíte přemýšlet nad tím, kam všude musíte kvůli této změně sáhnout.

Snaha po sdružení stejných či podobných metod a zabránění zdvojování stej-ného kódu bývá jedním z hlavních důvodů definice tříd, které jsou společným ro-dičem několika do té doby samostatných tříd.

Typickým příkladem skupiny tříd, která si přímo koleduje o společného rodi-če, jsou právě naše grafické třídy. Podíváte-li se na portfolia jejich metod, zjistíte, že všechny implementují metody požadované rozhraním IPosuvný a spolu s nimi další řadu posunových metod. Prohlédnete-li si ve zdrojových souborech těla těchto metod, zjistíte navíc, že jsou prakticky stejná.

Bylo by proto užitečné pro definovat všechny posuvné třídy společnou rodi-čovskou třídu a nazvat ji třeba Posuvný. V ní bychom definovali všechny metody, které by měl správný posuvný prvek obsahovat a které by pak mohli potomci této třídy zdědit. V druhém kroku bychom pak upravili všechny posuvné třídy tak, že bychom je definovali jako potomky tohoto rodiče a metody, které mohou od ro-diče zdědit, bychom z jejich těl odstranili.

Společný rodič Posuvný Pojďme si to vyzkoušet na třídách našeho projektu. Půjdeme spolu opět krok za krokem.

Příprava 1. Vytvořte novou standardní třídu, pojmenujte ji Posuvný a otevřete okno s je-

jím zdrojovým kódem.

2. Natáhněte k právě vytvořené třídě dědické šipky od tříd implementujících rozhraní IPosuvný a přidávajících k jím požadovaným metodám další posu-nové metody, tj. od tříd Obdélník, Elipsa, Trojúhelník, Čára, Text, Strom a od vaší třídy, kterou jste vytvořili ve druhé kapitole a postupně vylepšovali (má-

Page 355: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 355

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 355 z 433

te-li jich víc, tak od všech). Implementační šipky k rozhraní IPosuvný však za-tím nemažte.

Tyto třídy budu ve zbytku této podkapitoly označovat jako IP-třídy. Není to však žádný oficiální název. Je to pouze pracovní název pro skupinu tříd, na kterou se budu často odvolávat.

Možná se podivujete, proč jsem do seznamu nezařadil i třídy Výtah a Kabiny, které také implementují rozhraní IPosuvný. Hlavním úkolem in-stancí těchto tříd však není definovat a následně zobrazit nějaké geome-trické tvary či obrázky. Jejich instance mají předem zadané úkoly aimplementují rozhraní jen proto, aby mohly tyto úkoly splnit. Nebyloby proto vhodné, kdyby ovlivňovaly naše rozhodování o optimální po-době společné rodičovské třídy posuvných geometrických tvarů a ob-rázků.

Až budeme mít třídu Posuvný navrženou, můžeme se dodatečně rozhodnout, zda tyto třídy definujme jako její potomky, anebo zda zů-staneme u dosavadního stavu, kdy budou třídy přímými potomky tří-dy Object, které pouze implementují rozhraní IPosuvný.

3. Otevřete soubory se zdrojovými kódy právě označených dceřiných tříd (IP-tříd) a uspořádejte si je nějak rozumně na obrazovce, ať mezi nimi můžete snadno přepínat (nejlepší je uspořádat je do kaskády).

Šablona standardní prázdné třídy rozděluje řádkovými komentáři zdrojový sou-bor do sekcí. V následujících krocích budeme procházet zdrojovým kódem těchto tříd sekci za sekcí a v každé sekci popřemýšlíme, které z jejích atributů či metod bylo vhodné přestěhovat do společné rodičovské třídy.

V druhém kole pak budeme procházet jednotlivé dceřiné třídy a členy, které jsme zařadili do rodičovské třídy, z dceřiné třídy buď odstraníme, nebo je překry-jeme vlastní verzí.

Zkuste nejprve projít sami např. zdrojovým kódem třídy Obdélník a po-znamenejte si, co byste do společné rodičovské třídy přestěhovali. Pak se vraťte k následujícímu textu a porovnejte moje řešení se svým.

Page 356: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

356 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 356 z 433

Konstantní atributy třídy V první sekci jsou konstantní atributy třídy. Všechny třídy zde deklarují atribut AP, v němž uchovávají odkaz na aktivní plátno. Bylo by proto logické přestěhovat tento atribut do rodičovské třídy Posuvný a ze zdrojových kódů jednotlivých tříd jej odmazat.

Aby mohly dceřiné třídy k atributu přistupovat, nesmíme jej deklarovat jako soukromý. Pro naše účely bude optimálně jej deklarovat jako chráněný (protec-ted). /** Aktivní plátno, které dohlíží na správné vykreslení instance. */ protected static final AktivníPlátno AP = AktivníPlátno.getPlátno();

Proměnné atributy třídy Další sekce je vyhrazena proměnným atributům třídy. Zde nám šablona stan-dardní třídy nabízí deklaraci atributu počet, který bude počítat vytvořené instan-ce.

Tento atribut mají všechny IP-třídy. Dohodněme se proto, že budeme počítat všechny vytvořené tvary společným počitadlem (beztak pořadí instance využí-vám prakticky jen při ladění), které bude atributem jejich společné rodičovské tří-dě.

Na rozdíl od atributu AP ale ponechte počitadlo jako soukromé. Nestojíte pře-ce o to, aby jeho hodnotu nějaká dceřiná třída nepředvídatelně měnila. /** Celkový počet vytvořených instancí. */ private static int počet = 0;

Konstantní atributy instancí Pokračujme konstantními atributy instancí, kde nám šablona standardní třídy na-bízí celočíselný atribut počet, který v deklaraci hned také inicializuje. I tento atri-but necháme deklarovat rodičovskou třídu, avšak označíme jej jako protected, protože by se přístup k němu mohl instanci hodit a navíc jej instance beztak ne-může změnit. /** Rodné číslo instance = jako kolikátá byla vytvořena. */ protected final int pořadí = ++počet;

Definujte jej proto ve třídě Posuvný a v druhém kole jej v definicích jejích dceři-ných tříd smažeme.

IP-třídy mají v této sekci uveden atribut název, v němž si jejich instance ucho-vávají svůj název. I tento atribut bude nejlepší umístit do společné rodičovské tří-dy. Umístěte jej tam a poznamenejte si, že na něj nesmíme zapomenout v konstruktoru, protože tento atribut není (na rozdíl od svých předchůdců) v dekla-raci inicializován.

Page 357: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 357

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 357 z 433

/** Název sestávající z názvu třídy a pořadí instance */ private final String název;

Proměnné atributy instancí Poslední atributovou sekcí je sekce proměnných atributů instancí. Ta před nás po-staví první větší dilema.

Doposud byly atributy, které jsme „stěhovali do rodičovské třídy“ součástí všech dceřiných tříd. Atributy xPos a yPos, v nichž si instance některých pamatují svoji polohu, však již všechny IP-třídy nezavádějí. Instance všech IP-tříd musejí umět sdělit svoji polohu. Instance některých tříd ji uchovávají ve speciálních atri-butech, instance jiných tříd takovéto atributy nezavádějí a svoji polohu v případě potřeby nějakým způsobem na poslední chvíli zjistí.

Je na nás, zda se rozhodneme, že pro tuto polohu definujeme atributy ve spo-lečné rodičovské třídě, nebo jestli necháme na rozhodnutí tvůrce dceřiné třídy, zda pro zapamatování polohy použije nějaký reálný atribut, jak to dělá např. Obdélník, Elipsa a Trojúhelník, anebo zda zavede polohu jako fiktivní atribut do-stupný pouze přes přístupové metody, jak to dělá např. Strom..

V našem příkladu dáme přednost reálným atributům – každý posuvný objekt si bude pamatovat svoji polohu v atributech xPos a yPos. Deklarujeme proto tyto atributy ve třídě Posuvný a odstraníme je z IP-tříd, ve kterých jsou použity.

Je na našem rozhodnutí, zda tyto atributy deklarujeme jako soukromé či jako chráněné. Budou-li deklarovány jako soukromé, budou bezpečnější před nechtě-nými zásahy tvůrců dceřiných tříd, budou-li deklarovány jako chráněné, budou definice některých metod maličko jednodušší.

V praxi se dává většinou přednost bezpečnosti, takže i my deklarujeme tyto atributy jako soukromé. private int xPos; //Bodová x-ová souřadnice počátku private int yPos; //Bodová y-ová souřadnice počátku

Vedle toho, že budeme počítat s tím, že v druhém kroku tyto atributy z dceřiných tříd odstraníme, tak si i zde musíme poznamenat, abychom je v konstruktoru ne-zapomněli inicializovat.

Některé IP-třídy mají v této sekci definovány ještě další atributy, výšku, šířku a barvu. Protože se však tyto atributy na posunovatelnosti objektu nijak přímo nepodílejí, nebudeme je přesouvat a ponecháme je v jejich původních třídách.

Konstruktory Metody třídy IP-třídy nemají, tak se vrhneme rovnou na konstruktory. U společ-ných rodičovských tříd se většinou nepředpokládá, že aplikace bude vytvářet je-jich samostatné instance. Instance společných rodičovských tříd bývají většinou

Page 358: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

358 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 358 z 433

pouze podobjekty instancí jejich dceřiných tříd. Z toho můžeme vycházet i při rozhodování o tom, které typy konstruktorů definujeme.

Z předchozích kroků máme poznamenáno, že musíme inicializovat atributy xPos, yPos a název. Bylo by tedy vhodné definovat konstruktor, který umístí objekt na zadanou pozici. Definovat zbylé verze konstruktorů je zbytečné, protože se tím tvorba konstruktorů dceřiných tříd nijak nezjednoduší.

Pro tvorbu názvu zvolíme oblíbenou metodu jeho sestavení z názvu třídy ná-sledovaného podtržítkem a pořadím vytvoření instance. Využijeme k tomu meto-dy názevTřídy(Object), která je statickou metodou třídy P a která vrátí název třídy svého parametru. 1 2 3 4 5 6

public Posuvný( int x, int y ) { xPos = x; yPos = y; název = P.názevTřídy(this) + "_" + ++počet; }

Protože tento konstruktor bude zároveň jediným konstruktorem společné rodi-čovské třídy, musíme si poznamenat, že všechny konstruktory dceřiných tříd, kte-ré nepředávají hned řízení jinému konstruktoru stejné třídy, musí na počátku své definice volat tento rodičovský konstruktor.

Zároveň si poznamenáme, že máme z konstruktorů dceřiných tříd odstranit inicializace atributů xPos, yPos a název, leda bychom jim potřebovali přiřadit jiné počáteční hodnoty, než které jim přiřazuje rodičovská třída.

Metody instancí Nebudeme zde rozlišovat, ve které sekci se ta která metoda nachází. Do rodičov-ské třídy přesuneme ty metody, pro jejichž definici má rodičovská třída dostatek informací, a to jak přímých, nebo „zprostředkovaných“ prostřednictvím překrytí metod.

V našem příkladu budeme přesouvat metody mající na starosti pozici a po-sun dané instance, tj. následující metody: public int getX() public int getY() public Pozice getPozice() public void setPozice(int x, int y) public String getNázev() public String toString() public void posunVpravo(int vzdálenost) public void posunVpravo() public void posunVlevo() public void posunDolů(int vzdálenost) public void posunDolů()

Page 359: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 359

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 359 z 433

public void posunVzhůru()

Většinu uvedených metod můžeme do třídy Posuvný vložit beze změny jejich těla. Jedinou výjimkou je metoda toString(), která ve výstupním řetězci vrací i šířku, výšku a barvu objektu. O těch však třída Posuvný nemůže nic vědět. Zkrátíme pro-to definici metody a poznamenáme si, že v dceřiných třídách budeme muset do-plnit informace o atributech, i nichž rodičovská třída neví. 1 2 3 4

public String toString() { return název + ": x=" + xPos + ", y=" + yPos; }

Všechny uvedené metody budou od této chvíle definovány na jednom místě. Tím se výrazně zefektivní jejich případná následná úprava. Co je však v mnohých pří-padech důležitější: budou-li nám stačit jejich zděděné verze, nebudeme je muset v dalších dceřiných třídách definovat.

Vezměte tyto metody např. z elipsy nebo obdélníku a vložte je do kódu třídy Posuvný a upravte příslušně tělo metody toString().

Všechny třídy, pro něž definujeme společného rodiče, mají definovánutaké metodu nakresli(java.awt.Graphics2D). Metodu sice mají stejnou, ale v každé třídě je definována jinak. Proto ji prozatím do rodičovské třídy neumisťujeme.

Doladění dceřiných tříd Rodičovskou třídu tedy máme připravenou – můžete ji zkusit hned přeložit. Nyní musíme projít všechny její dceřiné třídy a jejich podobu doladit. Z předchozích kroků máme poznamenáno, že v každé z nich máme:

odstranit atributy deklarované v rodičovské třídě,

upravit definici konstruktoru, aby neinicializovala přesunuté atributy a místo toho na svém počátku zavolala správný konstruktor rodičovské třídy,

odstranit nebo překrýt metody definované v rodičovské třídě.

Elipsa, Obdélník, Trojúhelník Tyto třídy jsou si nesmírně podobné, tak je proberu najednou.

Odstranění přesunutých atributů nečiní potíže.

Page 360: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

360 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 360 z 433

Většina konstruktorů je definována tak, že volají jiný konstruktor téže třídy. Jedinou výjimkou je konstruktor s největším počtem parametrů, který např. u elipsy získá podobu:

1 2 3 4 5 6 7

public Elipsa( int x, int y, int šířka, int výška, Barva barva ) { super( x, y ); this.šířka = šířka; this.výška = výška; this.barva = barva; }

U obdélníku bude definován stejně a pouze u trojúhelníku bude bohatší o na-stavení zadaného směru.

Přístupové metody k atributům pozice i posunové metody jsou u všech tří tříd totožné s metodami, které jsme vkládali do rodičovské třídy, takže je můžeme bez obav z těla těchto dceřiných tříd odstranit.

Jedinou odchylkou je metoda toString(), kterou musíme překrýt modi-fikovanou verzí, v níž za řetězec vrácený rodičovskou verzí přidáme infor-mace o šířce, výšce a barvě objektu (u trojúhelníku navíc o jeho směru). Její verzi pro trojúhelník ukazuje následující program, verze pro elipsu a obdél-ník se liší pouze tím, že nevypisují žádný směr.

1 2 3 4 5

public String toString() { return super.toString() + ", šířka=" + šířka + ", výška=" + výška + ", barva=" + barva + ", směr=" + směr; }

Čára Třída Čára je velice podobná předchozím třídám. Nemá sice atributy uchovávající její výšku a šířku, ale uchovává místo nich souřadnice svého druhého koncového bodu. Postup její úpravy proto bude prakticky stejný a nebudu se zde o něm podrobněji rozepisovat.

Text Třída Text sice na první pohled vypadá skoro stejně jako předchozí čtyři, nicméně z našeho hlediska má jednu odchylku, kterou nesmíme přehlédnout. U této třídy je atribut název definován jinak než u jejích kolegyň: její instance do tohoto atribu-tu ukládají vypisovaný text.

Page 361: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 361

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 361 z 433

Atribut název je soukromý, takže k němu nemůžeme. Nám by ale nepomohlo ani kdyby byl chráněný či veřejný, protože je deklarován jako konstanta, takže jednou získaný obsah se již nedá změnit.

Nejjednodušším řešením tohoto problému je definovat svůj vlastní atribut (může se klidně jmenovat stejně), inicializovat jej v konstruktoru a překrýt meto-dy getNázev() a toString() vlastními verzemi: private final String název; public Text( String text, int x, int y, Barva barva ) { super( x, y ); this.název = text; this.barva = barva; this.font = new Font( "Dialog", Font.BOLD, 12 ); } public String getNázev() { return název; } public String toString() { return název; }

Strom Zbývá nám naše třída Strom.. Ta je oproti předchozím třídám trochu nestandard-ní, protože si např. neuchovává pozici v atributech a i nastavování pozice je u ní složitější. Probereme si proto jednotlivé požadované úpravy postupně:

Odstranění atributů deklarovaných v rodičovské třídě se dotkne pouze atributu název, protože atributy xPos a yPos třída vůbec nezavádí. Když už je ale od rodičovské třídy podědí, mohli bychom popřemýšlet nad tím, jestli to-ho nemůžeme někde využít.

Úprava definice konstruktoru bude pro strom spočívat pouze v přidání vo-lání rodičovského konstruktoru. Upravovaný konstruktor pak bude mít tvar:

1 2 3 4 5 6 7

public Strom_8(int x, int y, int šířka, int výška, int podílŠířkyKmene, int podílVýškyKmene) { super( x, y ); this.podílVýškyKmene = podílVýškyKmene; this.podílŠířkyKmene = podílŠířkyKmene; AP.nekresli();

Page 362: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

362 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 362 z 433

8 9

10 11 12

koruna = new Elipsa ( x, y, 1, 1, Barva.ZELENÁ ); kmen = new Obdélník( x, y, 1, 1, Barva.ČERVENÁ ); setRozměr( šířka, výška ); AP.vraťKresli(); }

Odstranění či překrytí zděděných metod nám ale dá trochu přemýšlení, pro-tože např. všechny přístupové metody jsou definovány jinak než v ostatních P-třídách. Tady ale právě využijeme toho, že rodičovský konstruktor uložil pozici do příslušných atributů a můžeme proto ponechat zděděnou verzi me-tod getX() a getY(), která vrací hodnoty uložené v těchto atributech.

Jedinou metodu, kterou budeme muset ve třídě Strom překrýt, je metoda setPozice(int,int), ve které potřebujeme příslušně přesunout korunu a kmen. Chceme-li dále využívat hodnot uložených v atributech xPos a yPos, nesmíme je zapomenout při té příležitosti nastavit.

1 2 3 4 5 6 7 8 9

public void setPozice(int x, int y) { AP.nekresli(); super. setPozice( x, y ); //Nastavujeme rodičovské atributy koruna.setPozice( x, y ); kmen .setPozice( x + (koruna.getŠířka() - kmen.getŠířka()) / 2, y + koruna.getVýška() ); AP.vraťKresli(); }

Výslednou podobu upravovaných tříd si můžete zkontrolovat podle vzorového řešení v projektu 08_Dědičnost_tříd_C.

Obrázek 8.9 Projekt 08_Dědičnost_tříd_C

Společný rodič Hýbací Možná vás už napadlo, že stejně, jako jsme udělali společného rodiče všech po-suvných tříd, bychom mohli udělat i společného rodiče všech hýbacích tříd. Do definice této rodičovské třídy bychom pak podle předchozího vzoru přesunuly atributy šířka a výška a s nimi i příslušné přístupové metody.

Máte pravdu, je to logické pokračování našeho úsilí. Proto jsem je pro vás na-chystal jako další úlohu.

Page 363: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 363

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 363 z 433

Definujte třídu Hýbací, která bude společným rodičem všech tříd im-plementujících rozhraní IHýbací, tj. tříd Elipsa, Obdélník, Trojúhelník a Strom..

Výslednou podobu upravovaných tříd si můžete zkontrolovat podlevzorového řešení v projektu 08_Dědičnost_tříd_D.

Obrázek 8.10 Projekt 08_Dědičnost_tříd_D

8.5 Abstraktní metody a třídy Už jsme svůj projekt hodně zestručnili, ale stále to ještě není dokonalé. Naše třídy dědí od svého společného rodiče leccos, ale nemohou do něj zdědit to, že imple-mentují rozhraní IPosuvný, resp. IHýbací, protože součástí implementace je i im-plementace metody nakresli(Java.awt.Graphics2D), kterou naše rodičovské třídy prozatím úspěšně ignorují.

Ono jim také nic jiného zatím nezbývá. Mohly by sice implementovat tuto metodu jako prázdnou nebo by při jejím spuštění mohly vyvolat nějaký chybový stav, ale to není optimální řešení. Takovýmto řešením bychom se totiž připravili o to, aby se případná chyba projevila již při překladu a zbytečně bychom její odha-lení odsunuly až do doby běhu se všemi z toho plynoucími negativními důsledky.

Potřebovali bychom nějakou konstrukci, která by nám dovolila oznámit, že dceřiné třídy implementují nějaké rozhraní a připravit jim k tomu vše potřebné s výjimkou metod, pro jejichž definici nemá rodičovská třída dostatek informací. Protože rodičovská třída neimplementuje všechny metody daného rozhraní, stává se ʺnedokonalouʺ a nemělo by být možné vytvořit její instance (jak by reagovaly, kdyby po nich někdo takovou metodu chtěl vyvolat?).

Protože takováto situace není zase až tak výjimečná, zavádějí objektově orien-tované jazyky tzv. abstraktní metody, což jsou metody, které jsou (stejně jako to dělá rozhraní) pouze deklarovány, avšak nejsou implementovány. Třída, která obsahuje nějakou abstraktní metodu se pak označuje jako abstraktní třídou a od ostatních tříd se liší tím, že nám překladač nedovolí vytvořit její instanci. Potká-te-li proto v programu instanci abstraktní třídy, může to být (stejně jako u rozhra-

Page 364: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

364 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 364 z 433

ní) jedině instance nějakého jejího potomka, který se za její instanci vydává (to po-tomci mohou).

Jak jsme si právě řekli, abstraktní třída nemůže mít vlastní instance, to mohou mít až její potomci, a to navíc jen ti, kteří již mají všechny meto-dy implementovány. Abychom tyto dva druhy tříd v textu odlišili,označujeme třídy, které mohou mít vlastní instance, jako konkrétní tří-dy.

Obdobně je to i s metodami. Metody, které mají implementaci, ozna-čujeme někdy jako konkrétní metody.

Abstraktní třída je vlastně takovým hybridem slučujícím vlastnostikonkrétní třídy a rozhraní. S konkrétní třídou má společné to, že jejídceřiné třídy od ní mohou dědit implementované metody (tj. mohoudědit implementaci), s rozhraním má zase společné to, že může metodypouze deklarovat a jejich implementaci tak svým konkrétním potom-kům vnutit, aniž by je musela sama implementovat.

Abstraktní metody bychom mohli rozdělit na dva druhy:

metody, které deklaruje některé z touto třídou implementovaných rozhraní, ale které třída neimplementuje,

metody, které třída sama deklaruje, ale neimplementuje.

Neimplementovaná metoda implementovaného rozhraní S prvním případem jsme se již potkali ve chvíli, kdy jsme zapomněli implemento-vat nějakou metodu rozhraní, k jehož implementaci se třída přihlásila. Tehdy to byla chyba, nyní to již může být záměr.

Natáhněte v diagramu tříd implementační šipky od třídy Posuvný k rozhraní IPosuvný a od třídy Hýbací k rozhraní IHýbací. Nyní se pokuste obě třídy přeložit. Překladač se vzbouří a ohlásí chybu, o které jsem se před chvílí zmiňoval.

Pomoc je jednoduchá: označte třídu jako abstraktní. Toho dosáhnete jedno-duše: mezi modifikátory v hlavičce třídy přidáte klíčové slovo abstract:

public abstract class Posunvý implements IHýbací public abstract class Hýbací extends Posuvný implements IHýbací

Page 365: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 365

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 365 z 433

Jak si můžete vyzkoušet, překladač se uklidní a vaši třídu přeloží. Když se podí-váte na diagram tříd, tak navíc zjistíte, že se v bloku dané třídy objevil stereotyp «abstract» příslušný blok se přebarví na růžovo.

Současně ale třída přestane nabízet možnost zkonstruovat svůj objekt. To se ale dalo očekávat. Před chvílí jsme si přece říkali, že vzhledem k tomu, že neim-plementuje všechny deklarované metody, bude třída považována za ʺnedokona-louʺ a nebude ji povoleno vytvářet instance.

Abychom na první pohled odlišili abstraktní třídy od konkrétních, za-vedeme si konvenci, podle které budeme před název abstraktních třídpřidávat předponu A. Přejmenujte proto naše nové abstraktní třídy na APosuvný a AHýbací.

Definujte pak tyto abstraktní třídy jako třídy implementující přísluš-ná rozhraní (tj. natáhněte implementační šipky) a ověřte, že překladač vše úspěšně přeloží.

Odstraňte poté všechny implementační šipky směřující od dceřinýchtříd k rozhraním a znovu projekt přeložte. I tentokrát by měl být pře-kladač vše přeložit bez problémů.

Obrázek 8.11 Projekt 08_Dědičnost_tříd_E

Nově deklarovaná abstraktní metoda Třída může také sama deklarovat abstraktní metodu, kterou sice sama implemen-tovat nemůže, ale všichni její konkrétní potomci by ji implementovat měli. Tako-vouto metodu deklarujete obdobně, jako byste ji deklarovali v rozhraní, jenom nesmíte zapomenout uvést správný modifikátor přístupu a mezi modifikátory musíte přidat klíčové slovo abstract – např.:

public void veřejnáAbstraktníMetoda( String parametr ); protected int chráněnáAbstraktníMetoda( int parametr );

K abstraktním metodám bych ještě uvedl pár poznámek:

Klíčové slovo abstract používáme proto, abychom překladači potvrdili, že středník se za hlavičkou neobjevil tak, že nám pouze upadla ruka na kláves-nici, ale že je zde schválně.

Page 366: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

366 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 366 z 433

Abstraktní metody nově deklarované v abstraktní třídě musí mít uveden modifikátor přístupu. Na rozdíl od rozhraní to však nemusí být pouze modi-fikátor public, ale můžete použít i modifikátor protected.

Deklarované abstraktní metody nemohou mít uveden modifikátor přístupu private, protože na ně potomci, kteří je mají překrýt, musí vidět.

Připomínám, že abstraktní metoda se od konkrétní metody liší tím, že si její deklarací vynucujete na všech potomcích, aby tuto metodu implementovali. Konkrétní metodu mohou potomci zdědit a vůbec se o ní nemusí starat, abs-traktní metodu musí implementovat stejně, jako musí implementovat všech-ny metody implementovaných rozhraní.

Poslední tvrzení si hned ověříme. Představte si, že bychom chtěli definovat nějaký speciální typ přesouvače, jehož rychlost přesunu by závisela na tom, jak velký ob-jekt přesouvá. Malé objekty by proto dokázal přesouvat rychleji než velké.

Aby mohl program rozhodnout, jak rychle má přesouvač přesouvat, potře-boval by znát plochu přesouvaného objektu. Definujte proto v třídě APosuvný abs-traktní metodu plocha(), která vrátí velikost plochy daného objektu.

public abstract int plocha();

Zkusíte-li nyní kteroukoliv z jejích konkrétních dceřiných tříd přeložit, překladač se opět vzbouří, protože v jejich zdrojových souborech nenalezne požadovanou implementaci.

Jakmile v některé třídě příslušnou metodu implementujete (její těl může klid-ně zůstat prázdné), překladač ji opět bez námitek přeloží.

Abstraktní třída bez abstraktních metod Zatím jsme abstraktnost třídy vynucovali tím, že jsme v ní nebo v jí implemento-vaných rozhraních deklarovali metody, které pak třída neimplementovala. Třídu však můžeme deklarovat jako abstraktní i bez toho, že by měla jakékoliv resty v oblasti neimplementovaných metod.

Jednou za čas se nám hodí definovat společnou rodičovskou třídu skupině tříd, avšak budeme nadále trvat na tom, aby se od této rodičovské třídy nevytvá-řely žádné instance. Pak stačí definovat danou třídu jako abstraktní nezávisle na tom, že žádné abstraktní metody neobsahuje. Překladač se k ní bude chovat stejně, jako k ostatním abstraktním třídám a nikomu nedovolí vytvořit její samostatnou instanci.

Page 367: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 367

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 367 z 433

8.6 Návrhový vzor Stav podruhé V podkapitole Návrhový vzor Stav (State) na straně 298 jsme se seznámili s návrho-vým vzorem Stav. V tehdejší realizaci zastupoval stavově závislou část instancí multistavové třídy jejich speciální atribut deklarovaný jako instance stavového rozhraní. Toto rozhraní pak implementovalo několik jednostavových tříd, z nichž každá popisovala chování objektu v jednom konkrétním stavu.

Při našich současných znalostech bychom mohli tehdejší architekturu vylep-šit o to, že bychom stavové rozhraní nahradili třídou (budu jí říkat stavová třída), jež by implementovala části kódu, které jsou všem jednostavovým třídám společ-né, a tím by definice jednostavových tříd zjednodušila. Protože ve stavovém roz-hraní budou určitě zbývat metody, které v této stavové třídě implementovány nebudou (kdyby v ní byly definovány všechny metody, nepotřebovali bychom dceřinné jednostavové třídy), bude tato stavová deklarována jako abstraktní.

Projekt Šipka Abyste si pocvičili deklaraci abstraktních metod a definici společné rodičovské třídy, připravil jsem pro vás malé cvičení:

Zkuste shrnout zkušenosti získané v předchozích dvou podkapitolách aupravte výše popsaným způsobem třídy řešící úlohu s otáčejícími se šipkami. Nahraďte rozhraní IŠipka abstraktní třídou AŠipka, která bude obsahovat implementací společných, stejně definovaných atributů ametod z jednostavových tříd VŠipka, SŠipka, ZŠipka a JŠipka.

Řešení úlohy je tak jednoduché, že se skoro stydím předvádět vzorové řešení. Ukazuji je pouze proto, že vím, že abstraktní třídy jsou pro vás nové a možná si budete chtít potvrdit, že jste vše správně pochopili. (Novou podobu multistavové třídy Šipka nepředvádím, protože se v ní změní pouze typ stavového atributu.) public abstract class AŠipka { //== KONSTANTNÍ ATRIBUTY TŘÍDY ================================================= protected static final AktivníPlátno AP = AktivníPlátno.getPlátno(); //== PROMĚNNÉ ATRIBUTY TŘÍDY =================================================== //== KONSTANTNÍ ATRIBUTY INSTANCÍ ============================================== protected final Trojúhelník špička;

Page 368: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

368 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 368 z 433

protected final Obdélník tělo; //############################################################################## //== KONSTRUKTORY A METODY VRACEJÍCÍ INSTANCE VLASTNÍ TŘÍDY ==================== //Prázdný implicitní konstruktor vyhovuje //== ABSTRAKTNÍ METODY ========================================================= public abstract int getX(); public abstract int getY(); public abstract void setPozice(int x, int y); public abstract AŠipka doleva(); public abstract void vpřed(); //== NOVĚ ZAVEDENÉ METODY INSTANCÍ ============================================= public void nakresli( Kreslítko g ) { špička.nakresli( g ); tělo .nakresli( g ); } }//public abstract class AŠipka

Z jednostavových tříd uvádím opět pouze definici třídy VŠipka. public class VŠipka extends AŠipka { //############################################################################## //== KONSTRUKTORY A METODY VRACEJÍCÍ INSTANCE VLASTNÍ TŘÍDY ==================== public VŠipka( int x, int y, Barva barva ) { int krok = AP.getKrok(); int k2 = krok/2; špička = new Trojúhelník( x*krok + k2, y*krok, k2, krok, barva, Směr.V ); tělo = new Obdélník ( x*krok, y*krok + krok/4, k2, k2, barva ); } //== PŘÍSTUPOVÉ METODY ATRIBUTŮ INSTANCÍ ======================================= public int getX() { return tělo.getX(); } public int getY() { return špička.getY(); } public void setPozice(int x, int y) { int krok = AP.getKrok();

Page 369: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 369

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 369 z 433

špička.setPozice( x + krok/2, y ); tělo .setPozice( x, y + krok/4 ); } //== PŘEKRYTÉ ABSTRAKTNÍ METODY RODIČOVSKÉ TŘÍDY =============================== public AŠipka doleva() { int krok = AP.getKrok(); return new SŠipka( getX()/krok, getY()/krok, špička.getBarva() ); }//public IŠipka vlevoVbok() public void vpřed() { setPozice( getX()+AP.getKrok(), getY() ); }//public void vpřed() }//public class VŠipka

Jak vidíte, moc se toho nezměnilo. Do rodičovské třídy se přesunuly pouze atribu-ty a metoda nakresli(Kreslítko).

Zajímavější situace by nastala ve chvíli, kdy bychom se rozhodli, že by bylo efektnější přesouvat šipka pomocí přesouvače. Pak by bylo rozumné, aby metodu vpřed() implementovala pouze metoda AŠipka s tím, že dceřiné třídy budou místo metody vpřed() implementovat metodu před(), která bude vracet pozici, na kte-rou by se měla daná šipka přesunout při pohybu o jedno pole vpřed. Třída AŠipka by pak příslušnou instanci přesunula do této cílové pozice pomocí přesouvače.

8.7 Co je na dědičnosti špatné Název podkapitoly vás možná zarazil. Celou dobu vám tu vykládám o dědičnosti tříd a popisuji její nejrůznější vlastnosti a najednou vložím podkapitolu, kde se nejspíš chystám dědičnost pomlouvat.

Bohužel, je tomu tak. Největší slabinou dědičnosti tříd je to, že dědičnost na-rušuje to, co si na objektově orientovaném programování ceníme nejvíce, a to je zapouzdření. V úlohách, které jsme v této kapitole řešili, jsme se o tom mohli ne-jednou přesvědčit.

V mnoha případech jsme mohli správně naprogramovat překrývající dceři-nou metodu pouze tehdy, když jsme měli podrobné informace o tom, jak je na-programována její rodičovská třída. Za všechny bych např. uvedl problémy s umísťováním našich xobdélníků v pasáži Metoda setPozice(int,int) na straně 346.

Do obdobných problémů bychom se dostali, kdybychom se při definici třídy Terč rozhodli překrýt metodu setRozměr(int,int) a nevěděli (nebo na to zapo-

Page 370: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

370 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 370 z 433

mněli), že rodičovská třída Kruh definuje její jednoparametrickou přetíženou verzi setRozměr(int) tak, že volá metodu setRozměr(int,int) s oběma parametry stej-nými. Kdybychom pak upravovali velikost největšího kruhu (ten je rodičovským podobjektem terče) tak, že bychom zavolali metodu setRozměr(int), tak by se nám celý program zhroutil. Tato metoda by totiž zavolal onu dvouparametrickou, kterou ale právě překrýváme, takže by vlastně zavolal metodu, která volá ji, a už by se program vrtěl jako nudle v bandě.

Jak jsme si ale před tím mnohokrát říkali, třída by měla být naprogramována tak, aby nikdo neznal způsob implementace jednotlivých metod. Bude-li s třídou a jejími instancemi někdo komunikovat pouze na základě zveřejněného rozhraní.

Budete-li chtít poslední pravidlo dodržet, musíte do rozhraní (přesněji do kontraktu) zahrnout všechny informace o implementaci, které by měl případný tvůrce dceřiných tříd vědět. Jakmile tak učiníte, musíte počítat s tím, že rozhraní je nedotknutelné. Co se jednou objeví v rozhraní a příslušném kontraktu, musí tak už zůstat navždy (přesněji mělo by). Než proto svoji třídu zveřejníte a dáte ji-ným programátorům v plen, dobře si rozmyslete, jak bude její rozhraní vypadat.

8.8 Kdy (ne)použít dědičnost Při tvorbě tříd, jejichž instance mají vztah k instancím jiných tříd, používám dva mechanizmy:

kompozici, neboli skládání, kdy instance souvisejících tříd vystupují jako atributy nově vytvořené třídy (takto byla konstruována např. třída Strom),

dědičnost, při níž jsou instance nově vytvářené třídy speciálním případem in-stancí původní třídy (takto byly vytvářeny třídy Čtverec a Kruh).

Mnozí začátečníci (a nejen začátečníci) jsou natolik uchváceni možnostmi dědič-nosti tříd, že jí používají i tam, kde je to naprosto nevhodné a ke by bylo daleko výhodnější použít skládání. Autor jedné knihy o objektovém programování např. uváděl příklad žáka, který za ním přišel s dotazem, jak má naprogramovat třídu Letadlo, která je potomkem třídy Křídlo, když letedlo má křídla dvě.

Již na počátku kapitoly jsem říkal, že řada programátorů si plete význam a účel dědičnosti. Neberou dceřinou třídu jako něco, co specifikuje podmnožinu in-stancí rodičovské třídy, ale jako něco, co od svého rodiče zdědí řadu výhodných atributů a metod a k těmto zděděným atributům a metodám rodičovské třídy přidá další.

Dceřiné třídy opravdu často něco přidávají (vzpomeňte si na dvojici tříd Posuvný – Hýbací), ale často naopak možnosti svých rodičů omezují (typickým pří-

Page 371: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 371

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 371 z 433

kladem jsou třídy Čtverec a Kruh). Nicméně přidávání atributů a/nebo metod ne-smí být hlavním důvodem pro rozhodnutí o definici nové třídy jako potomka tří-dy stávající.

Obecně bychom měli říci, že v prvním plánu byste měli uvažovat o skládání a k úvahám o dědičnosti přistoupit jet tehdy, když si o to vlastnosti tříd vysloveně „koledují“. Teď možná ještě budete často na pochybách, co zvolit. Věřím ale, že v průběhu zbytku učebnice budete mít dost příležitostí se setkat s oběma druhy ko-operace tříd, abyste se naučili ve své budoucí praxi rozhodovat optimálně.

Co jsme dělali špatně A hned začnu tím, že pomluvím to, co jsem vám před chvílí ukazoval. V pasáži Složitější dceřiná třída na straně 344 jsme konstruovali třídu XObdélník jako potom-ka třídy Obdélník a v následující úloze jsem vám zadal zkonstruovat třídu Terč ja-ko potomka třídy Kruh.

Obě tyto třídy jsme definovali jako dceřiné proto, abych vám na jejich kon-strukci mohl ukázat různé záludnosti, které vás mohou při tvorbě dceřiných tříd potkat. Doufal jsem přitom, že vnější grafická podoba instancí ve vás vyvolá do-jem, že definovat tyto třídy jako potomky doporučených rodičů je správné. A vi-díte, není.

Když nad tím budete chvíli přemýšlet, určitě dospějete k závěru, že xobdél-ník opravdu není speciálním případem obdélníku a terč speciálním případem kruhu. Oba totiž nabourávají jednu ze základních vlastností těchto tvarů, se kte-rou všechny třídy, které dané tvary používaly (např. náš Strom) počítaly. Touto vlastnosti byl referenční bod, vůči kterému se počítala pozice obrazce.

U obdélníku i kruhu se jejich pozice počítala jako pozice levého horního rohu opsaného obdélníku, kdežto naše dceřiné třídy definovaly jako vztažný bod střed své grafické reprezentace. Kdybychom proto použili terč a xobdélník při kon-strukci stromu, vznikl by útvar, který by byl daleko abstraktnější, než byl náš pů-vodní záměr.

Obě třídy, tj. Xobdélník i Terč, definovaly rozšíření svých rodičů obdobně, ja-ko výše pomlouvaný obdélník a úsečka definovaly rozšíření bodu. Kdybych za-pomněl na výukové důvody a díval se na úlohu jenom programátorsky, tak bych s definicí těchto tříd počkal, až bude definována třída Hýbací, resp. AHýbací, defi-noval obě třídy jako jejího potomka a potřebný obdélník, resp. vnější kruh defino-val jako atributy. (Zkuste si to a uvidíte, jak se leccos zjednoduší.)

Oba příklady jsou tu uvedeny jako názorný příklad toho, jak svůdné může být někdy použití dědičnosti a do jakých problémů se dostanete, pokud těmto svodům podlehnete. Otestujete-li třídy XObdélník a Kruh nyní po zavedení tříd

Page 372: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

372 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 372 z 433

APosuvný a AHýbací, zjistíte, že zase nechodí. Zkuste je rozchodit a uvidíte, že to nebude úplně triviální.

Definujte třídy XobdélníkP a TerčP jako potomky třídy AHýbací a porov-nejte jejich definice s definicemi jejich předchůdců.

Kdy dát přednost skládání a kdy dědičnosti Dědičnost jsme tedy pomluvili, ale v řadě případů je to stále nejlepší možné řeše-ní. Povězme si proto pár záchytných bodů, podle kterých byste se mohli rozhodo-vat, zda v daném případě dát přednost spíše skládání objektů, anebo zda pro vás bude výhodnější využití dědičnosti.

Mějte na paměti, že ve většině případů je daleko výhodnější (a často jediné rozumné) použití skládání. O využití skládání byste proto měli přemýšlet nejdří-ve. Rozhodnete-li se použít dědičnost, měli byste pro to mít pádné důvody.

První otázkou, kterou byste si měli položit je: Budu potřebovat přetypovat instanci na předka? Odpovíte-li si na ni ANO, bude nejlepším řešením využití dědičnosti. Odpovíte-li si NE, budete se ptát dál.

Další otázku, kterou byste si měli položit je: Je definovaná třída speciálním případem své rodičovské třídy? Jinými slovy: jsou instance vytvářené třídy pod-množinou instancí rodičovské třídy? Pokud ANO, použijte dědičnost. Pokud ne, bude nejspíše výhodnější použít skládání.

8.9 Shrnutí – co jsme se naučili Pro podmnožinu instancí nějaké třídy majících speciální vlastnosti můžeme zavést vlastní třídu.

Tuto třídu označujeme jako dceřinou, odvozenou nebo jako podtřídu.

Jejím protějškem je rodičovská třída označovaná také jako bázová či základní třída a nebo jako nadtřída.

V jazyku Java má každá třída právě jednoho rodiče (ani víc, ani méně). Jedi-nou výjimkou je systémová třída Object, která je společným (pra)rodičem všech ostatních tříd a sama již žádného rodiče nemá.

Dceřiná třída dědí od své rodičovské třídy všechny atributy a metody, a to i soukromé, i když k těm nemá přístup.

Page 373: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 373

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 373 z 433

Každá instance dceřiné třídy v sobě obsahuje podobjekt své rodičovské třídy.

Při konstrukci instance se nejprve zavolá konstruktor jejího rodičovského po-dobjektu.

Konstruktor lze zavolat zapsáním klíčového slova super následovaného se-znamem parametrů v závorkách. Není-li tento příkaz použit, pokusí se pře-kladač zavolat bezparametrický rodičovský konstruktor.

Volání rodičovského konstruktoru musí být naprosto prvním příkazem těla konstruktoru.

Není-li volaný rodičovský konstruktor dostupný (neexistuje nebo je private), ohlásí překladač chybu.

V jednom konstruktoru není možné volat současně super a this.

Při vytváření konstruktorů dceřiné třídy je vhodné nezapomenout na kon-struktor, který využívá všech možností konstruktoru rodičovské třídy, proto-že jinak bude tento rodičovský konstruktor pro potomky v další generaci nedostupný.

Vedle modifikátorů přístupu private a public existuje i modifikátor protected. Jím označené atributy a metody jsou přístupné ostatním třídám v daném projektu a v jiných projektech pouze potomkům dané třídy.

Protože atributy a metody třídy kvalifikujeme názvem třídy, budou statické složky rodičovské třídy dostupné i v případě, že dceřiná třída definuje stejně pojmenované statické složky.

Dceřiná třída může ke zděděným atributům a metodám přidat atributy a me-tody vlastní.

Definuje-li instance stejnou metodu jako je metoda zděděné od rodičovské třídy, zakryje tato nová metoda metodu zděděnou.

Překrývající metoda bude použita i v případech, kdy je v definici rodičovské třídy původně použita metoda překrytá.

Překrytou verzi metody lze vyvolat prostřednictví klíčového slova super.

V definicích metod je třeba pamatovat na to, že dceřiné třídy mohou metodu překrýt a umožnit jim tohoto překrytí využít.

V prostředí BlueJ lze vztah dědičnosti tříd zadat natažením dědické šipky a zrušit odstraněním této šipky.

Page 374: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

374 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 374 z 433

Máme-li několik tříd s velmi podobnými vlastnostmi, může být výhodné pro ně definovat společnou rodičovskou třídu, do které se přesunou společné atributy a metody.

Třídu můžeme deklarovat jako abstraktní. Abstraktní třída nemůže vytvářet vlastní samostatné instance. Její instance mohou být pouze podobjekty jejích dceřiných tříd.

Třídu, která není abstraktní, označujeme jako konkrétní.

Abstraktní třída nemusí implementovat všechny metody implementovaných rozhraní.

Abstraktní třída může deklarovat vlastní abstraktní metody, které pouze de-klaruje, ale neimplementuje. Jejich implementaci přenechává svým konkrét-ním potomkům.

Abstraktní třída je slučuje vlastnosti třídy a rozhraní. Její potomci od ní mo-hou dědit jak implementaci, tak povinnost něco implementovat sami.

Při konstrukci nových tříd jejichž instance mají nějaký vztah k současným třídám se musíme rozhodnout mezi použitím skládání a dědičnosti.

Ve většině případů je výhodnější použití skládání než použití dědičnosti.

Dědičnost bychom měli použít opravdu jen tehdy, budou-li instance dceřiné třídy speciálními případy instancí rodičovské třídy.

Pádným argumentem pro použití dědičnosti je očekávaná potřeba přetypovat instanci na předka.

Při rozhodování o použití dědičnosti bychom neměli podlehnout svodům vnější podobnosti instancí.

Nově zavedené termíny abstraktní metoda abstraktní třída bázová třída dceřiná třída chráněný přístup nadtřída odvozená třída podobjekt podtřída

Page 375: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 8: Třídy mohou také dědit 375

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 375 z 433

prarodič protected překrytá metoda překrytí metod překrývající metoda rodič rodičovská třída rozšíření třídy základní třída

Page 376: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

376 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 376 z 433

9. Budete si to přát zabalit?

Kapitola 9 Budete si to přát zabalit?

☯ Co se v kapitole naučíme V této kapitole.

9.1 Velké programy a jejich problémy Jakmile začne složitost programu narůstat a tříd začnou být desítky, stovky a tisí-ce1, přestává být únosné udržovat všechny třídy po kupě, protože by se v nich br-zy nikdo nevyznal. Je to obdobné, jako když máte všechny papíry na jedné hromadě na stole nebo když máte všechny soubory uložené v jedné složce.

Zkušenost ukázala, že doba potřebná k vytvoření programu, roste exponen-ciálně s jeho velikostí. Budete-li schopni vytvořit v náhlém záchvatu geniality za jeden den tisíciřádkový program, vůbec z toho nevyplývá, že byste byli schopni vytvořit za týden 5000řádkový program a za měsíc 20000řádkový. Lze spíše oče-kávat, že k vytvoření 5000řádkového programu budete potřebovat skoro měsíc a 20000řádkový program nevytvoříte o moc rychleji než za rok.

Jedním z cílů správného návrhu je rozdělit program na několik menších částí, které spolu budou pouze minimálně souviset. Každou část je pak možno vyvíjet relativně samostatně a ve vhodných intervalech pouze přezkušovat správnou spo-lupráci jednotlivých částí.

Jednou takovou částí je třída. Je to však příliš malá část (i když může mít i několik tisíc řádků) a při tvorbě opravdu velkých programů s takovýmto dělením

1 Samotná standardní knihovna Javy měla v době psaní knihy 12 798 (slovy dvanáct tisíc

sedm set devadesát osm) tříd. Z nich je 3270 tříd veřejných a začleněných do standardní do-kumentace.

Page 377: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 377

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 377 z 433

nevystačíme (jak brzy poznáte, nevystačíme s ním dost dobře ani u programů menších). Potřebujeme mít možnost vytvářet části vyššího řádu.

Při správném rozdělení programu můžeme s výhodou využít i to, že uvnitř části si mohou třídy navzájem před sebou odhalit část roušky zapouzdření, aby pak tyto znalosti mohly využít ke zvýšení efektivity svojí práce. Přepokládá se přitom, že všechny třídy uvnitř dané části jsou dílem jednoho nebo několika málo programátorů, kteří jejich kód důvěrně znají a riziko nekorektní modifikace kódu je u nich proto výrazně menší.

9.2 Balíčky Java umožňuje sdružovat skupiny tříd do hierarchicky uspořádaných balíčků (package). Při ukládání zdrojových souborů na disk je přitom dobrým zvykem (a většina vývojových prostředí na tom dokonce trvá), že zdrojové soubory tříd pat-řících do společného balíčku se ukládají do stejné složky. Překladač potom stej-ným způsobem uspořádá i přeložené soubory.

Zdrojové a přeložené soubory mohou, ale nemusí být ve stejné složce.BlueJ umísťuje přeložené soubory (soubory s příponou class) do stejné složky jako zdrojové soubory, ale většina profesionálních prostředí dá-vá přednost oddělenému umístění zdrojových, přeložených a testova-cích souborů.

K tomu, aby překladač uznal třídu jako součást balíčku, nestačí pouhé umístění zdrojového textu do příslušné složky. Programátor ještě musí explicitně potvrdit, že soubor sem nezabloudil omylem, ale že sem opravdu patří. Proto je potřeba, aby zdrojový text začínal příkazem:

package nazev_balíčku;

kde nazev_balíčku musí být stejný jako název složky, v níž je soubor umístěn. Pro-tože název balíčku je identifikátorem, logicky z toho vyplývá, že názvy složek s balíčky musí dodržovat pravidla pro tvorbu identifikátorů, tj. smějí obsahovat pouze písmena, číslice a znaky „_“ a „$“, nesmějí začínat číslicí a nesmějí být shodné s žádným klíčovým slovem. Podle konvence by měl navíc název balíčku obsahovat pouze malá písmena (je to sice jenom konvence k jejímuž dodržování vás překladač nenutí, ale je všeobecně dodržovaná).

Page 378: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

378 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 378 z 433

I u názvů balíčků platí, že Java rozlišuje velikost znaků. Pracujete-li ve Windows, tak víte, že tento operační systém velikost znaků v názvechsouborů a složek sice zobrazí, ale jinak ji nebere na vědomí. Složka se-rial a SERIAL je pro něj jedna a ta samá složka. Ne tak pro Javu. V pří-kazu import proto musíte použít přesně stejnou velikost znaků, jakoumají názvy příslušných souborů a složek.

Tady musím opět upozornit na problémy s češtinou. Některá prostředí totiž mají problémy s převodem kódů a názvy souborů a složek nejsou schopna správně rozpoznat.

Jak jsem již řekl, příkaz package musí být prvním příkazem v souboru, před ním smějí předcházet pouze mezery a komentáře. Třída může být navíc pouze v jedi-ném balíčku (jeden a tentýž zdrojový soubor nemůže být zároveň ve dvou slož-kách) – proto také může být na začátku třídy pouze jediný příkaz package.

Podbalíčky Stejně jako mohou složky obsahovat podsložky, mohou balíčky obsahovat podba-líčky. Název podbalíčku se skládá z názvu rodičovského balíčku následovaného tečkou a názvem podbalíčku.

Podbalíčky mohou mít opět podbalíčky, a to do libovolné hloubky. Pro název platí stále stejná konvence. Balíčky tak mohou vytvářet stejnou stromovou struk-turu jako složky a i pro jejich názvy platí obdobná pravidla, jako pro absolutní cestu k dané složce – pouze se lomítka nebo zpětná lomítka (záleží na operačním systému) nahradí tečkami.

Balíčky a podbalíčky jsem používal i při přípravě doprovodných programů a příkladu k prvním osmi kapitolám této učebnice. Pak jsem vám sice potřebné programy sesypal do jednoho projektu, takže vy jste měli všechny třídy pěkně na hromádce, ale chtěl-li jsem se v jednotlivých etapách vývoje programů trochu vy-znat, pro sebe jsem si je do balíčků rozmístit musel.

Page 379: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 379

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 379 z 433

Uspořádání podbalíčků s programy k dosavadní části knihy Podívejte se např. na to, jsou doprovodné pro-gramy a řešené úlohy k dosavadním kapitolám uspořádány

Všechny zdrojové soubory umisťuji do ba-líčku-složky rup.

V tomto balíčku jsou podbalíčky (podslož-ky) určené jednotlivým jazykům, do nichž se učebnice překládá. Nás bude nyní zají-mat podbalíček nazvaný česky, tj. balíček rup.česky.

V balíčku rup.česky jsou podbalíčky další úrovně věnované jednotlivým kapitolám – např. třetí kapitole je věnován podbalíček rup.česky._02_třídy.

Uvnitř balíčku věnovaného kapitole jsou další podbalíčky věnované jednotlivým probíraným oblastem.

V balíčcích kapitol první a druhé části většinou naleznete podbalíček (= pod-složku) tvary, v němž jsou umístěny třídy přímo související a naším kreslí-cím plátnem. Pokud se v průběhu kapi-toly podoba tříd v balíčku změní, může zde být takovýchto balíčků i několik.

V každé kapitole nalezete i podbalíček příklady, v němž jsou umístěny jedno-dušší příklady dané kapitoly.

Jsou-li součástí kapitoly větší příklady sestávající z několika tříd, mívají větši-nou svůj vlastní balíček.

Za balíčky jednotlivých kapitol následuje balíček rup.česky.společně a balíček rup.česky.tvary. Do takovýchto samostat-ných balíčků umisťuji třídy v okamžiku, kdy jsme je již vypiplali do více mé-ně konečné podoby, ve které mají šanci nějakou dobu zůstat.

Obrázek 9.1

Stromová struktura balíčků počátečních kapitol projektu

Page 380: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

380 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 380 z 433

Obdobných zásad se budu držet i při umísťování programů pro tuto a následující kapitoly. Jediným rozdílem bude, že od této chvíle vám již nebudu setřásat třídy na jednu hromádku a vytvářet nový projekt bez balíčků, ale ponechám třídy v původních balíčcích.

Žáci se mne občas ptají, kolik tříd by mělo být v jednom balíčku. Tentopočet závisí na tom, jak složitou úlohu třídy z daného balíčku řeší. Ve standardní knihovně je 10 balíčků, které obsahují jedinou třídu a 12 ba-líčků se dvěma třídami. Vedle nich je tu ale také řada balíčků s několika desítkami tříd.

9.3 Balíčky a BlueJ Asi se budete ptát, jak počítač pozná, kde má strom balíčků a podbalíčků svůj ko-řen. Odpověď je jednoduchá: sám to nepozná, musíme mu to nějak sdělit.

Každé vývojové prostředí definuje svůj způsob, jak mu oznámíme, ve které složce se tento kořen nachází. BlueJ zachází s balíčky oproti jiným vývojovým prostředím poněkud nestandardně: každý balíček je po něj projektem (a jako ta-kový musí obsahovat soubor bluej.pkg). Kořenovou složkou stromu balíčků je pak ta složka, jejíž rodičovská složka již neobsahuje soubor bluej.pkg a pro BlueJ proto není projektem.

Nyní si již můžete sami odvodit, že název souboru bluej.pkg vznikl ze sousloví BlueJ package – v BlueJ totiž můžeme do jisté míry považovat balíček za projekt a projekt za balíček nebo přesněji strom balíčků.

Balíčky můžeme (stejně, jako většinu jiných věcí) připravovat „ručně“ za pomoci nějakého správce souborů, nebo automaticky pomocí prostředků nabízených pro-středím BlueJ.

Příprava stromu balíčků pro BlueJ ve správci souborů Ruční příprava stromu balíčků je poměrně jednoduchá:

1. Rozmyslete si, jak bude vypadat struktura balíčků vašeho projektu.

Page 381: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 381

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 381 z 433

2. Ve svém oblíbeném správci souborů vytvořte kořenovou složku budoucího stromu balíčků. Přitom zkontrolujte, že složka, v níž tuto kořenovou složku vytváříte, neobsahuje soubor bluej.pkg.

3. V čerstvě vytvořené kořenové složce vytvořte strukturu složek odpovídající požadované struktuře balíčků.

4. V kořenové složce vytvořte prázdný soubor bluej.pkg nejlépe např. textovým editorem).

5. Tento soubor zkopírujte do všech jejích podsložek odpovídajících balíčkům.

9.4 Hrajeme si s balíčky Vyzkoušíme si vše hned na nějakém jednoduchém příkladu a vytvoříme jednoduchý strom balíčků podle obr. 9.2. Postu-pujte podle následujícího návodu:

1. Otevřete složku, do které umísťujete svoje projekty.

2. Vytvořte v ní složku Kořen, která bude kořenovou slož-kou vytvářeného stromu balíčků.

3. Ve složce Kořen vytvořte prázdný textový soubor pojmenovaný bluej.pkg. Pracujete-li ve Windows. můžete např. klepnout pravým tlačítkem myši v

prázdné oblasti a v místní nabídce zadat příkaz Nový a v následně rozbalené podnabídce pak příkaz Textový dokument (viz obr. 9.3). Windows na vás sice vy-bafnou s dialogovým oknem, v němž budou tvrdit, že změníte-li příponu souboru, může se stát, že soubor nebude možno použít, ale tím se nenechte zmást a své zadání potvrďte.

Obrázek 9.2

Cvičný strom balíčků

Page 382: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

382 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 382 z 433

Obrázek 9.3 Vložení prázdného textového dokumentu

4. Ve složce Kořen vytvořte podložku kap_1 (budeme se tvářit, že připravujeme balíček pro první kapitolu knihy).

5. Do nově vytvořené podsložky zkopírujte soubor bluej.pkg.

6. Otevřete složku Kap_1 a vytvořte v ni podsložky příklady a tvary.

7. Do složky tvary zkopírujte z projektu 02_Objekty soubory s příponami .java a .pkg.

Tím jste s přípravami hotovi a můžete začít zkoušet. Spusťte BlueJ a příkazem Pro-jekt → Otevřít projekt… jej požádejte, aby otevřel projekt. V následně otevřeném dialo-govém okně mu pak zadejte právě vytvořený projekt Kořen.

BlueJ vás asi překvapí tím, že neotevře okno projektu Kořen, ale správně od-hadne, že kořenový balíček je nezajímavý (není v něm žádní třída a obsahuje pou-ze jediný podbalíček) a že obdobně nezajímavý je i jeho podbalíček kap_1 a otevře proto rovnou okno podbalíčku tvary. ve kterém již najde nějaké třídy. Název ba-líčku, jehož okno je právě otevřeno, pak pro snazší orientaci uvede v záhlaví okna v hranatých závorkách za názvem projektu – viz obr. 9.4.

Page 383: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 383

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 383 z 433

Obrázek 9.4 Diagram tříd balíčku kap_1.tvary

Rodičovský balíček v diagramu tříd V okně projektu zobrazí BlueJ známé ikony z našeho prvního projektu (aby ne, když jsme sem příslušné soubory zkopírovali). Kromě nich ale pod ikonou třídy Směr prosvítá ikona připomínající ikonu používanou ve správci souborů pro označení složky. To je ikona balíčku a v tomto diagramu zastupuje rodičovský ba-líček.

Odsuňte ikonu třídy Směr doprava a uvidíte, že na ikoně balíčku je text <go up>, symbolizující, že se jedná o ikonu zastupující rodičovský balíček aktuál-ního podbalíčku. Ikonu rodičovského balíčku není možné nikam přesunout, takže se jí musí zbytek diagramu tříd přizpůsobit.

Poklepáním na ikonu balíčku otevřete nové okno s diagramem tříd tohoto ba-líčku. Ten obsahuje vedle ikony doprovodného textového dokumentu pouze dvě ikonu: ikonu balíčku tvary a ikonu rodičovského balíčku. Pokud otevřete i ten, zjistíte, že obsahuje pouze ikonu balíčku kap_1.

Page 384: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

384 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 384 z 433

Převedení obyčejné složky na balíček v BlueJ Jistě si vzpomenete, že jsme ve složce kap_1 vytvořili vedle složky tvary také složku příklady. Tu nám však BlueJ v diagramu tříd neukázal. Není to proto, že by v ní ne-byly žádné zdrojové soubory. Je to proto, že v této složce není soubor bluej-pkg a BlueJ ji proto nepovažuje za balíček.

Abychom to dokázali, zavřete BlueJ přesuňte soubor Směr.java ze složky tvary do složky příklady. Pak BlueJ znovu otevřete – uvidíte, že se nic nezměnilo s výjim-kou toho, že přesunutá třída bude v diagramu projektu kap_1.tvary chybět.

Zkusíme to napravit. Zavřete znovu BlueJ a zkopírujte do složky příklady sou-bor bluej.pkg. Je jedno, odkud to bude, protože si jej BlueJ beztak upraví. Důležité je, že tam nějaký bude. Zkuste nyní znovu otevřít v BlueJ projekt Kořen.

Tentokrát již BlueJ nedoběhne až do balíčku kap_1.tvary, ale zastaví se hned v balíčku kap_1, protože zde má najednou dva podbalíčky a není tedy zcela jasné, kudy by se měl dál vydat

Poklepáním na ikonu otevřete balíček příklady a měli byste v něm najít ikonu třídy Směr, kterou jsme sem před chvílí přesunuli.

Vytvoření nového balíčku v BlueJ Nové balíčky nemusíme vytvářet pouze ve správcích souborů. Můžeme je vytvo-řit i přímo v BlueJ:

1. Zadejte příkaz Úpravy → Nový balíček.

2. BlueJ otevře dialogové okno, v němž se vás zeptá na název vytvářeného ba-líčku. Zadejte např. zvláštní.

3. V diagramu tříd se objeví nová ikona balíčku pojmenovaná zvláštní. Podívá-te-li se ve správci souborů do složky příklady, zjistíte, že v ní BlueJ vytvořil podsložku zvláštní a otevřete-li ji, naleznete v ní prázdný soubor bluej.pkg.

Putování stromem balíčků Klepněte v diagramu tříd pravým tlačítkem na ikonu balíčku zvláštní a otevřete její místní nabídku. Naleznete v ní dva příkazy: Otevřít a Odstranit. Zadejte příkaz Otevřít a otevřete tak okno s diagramem tříd daného balíčku. Budou v něm pouze dvě ikony: ikona doprovodného textového soubor a ikona rodičovského balíčku.

Klepněte pravým tlačítkem na ikonu rodičovského balíčku a rozbalte tak její místní nabídku. Jak vidíte (viz obr. 9.5), místní nabídka rodičovského balíčku se od nabídky ʺnerodičovskéhoʺ balíčku liší:

Page 385: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 385

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 385 z 433

Neobsahuje příkaz Odstranit.

Místo položky Otevřít obsahuje celou sadu příkazů umožňující otevřít kterýko-liv z (pra)rodičovských balíčků včetně kořenového nepojmenovaného balíč-ku.

Obrázek 9.5 Místní nabídka rodičovského balíčku umožňuje otevřít kterýkoliv z (pra)rodičovských balíčků

Zkuste si otevřít jednotlivé balíčky uvedené v místní nabídce a ověřte si, že při žádosti o otevření balíčku, jehož okno je již otevřeno, neotevře BlueJ nové okno, ale pouze aktivuje okno otevřené.

Automatická redefinice příkazu package Na počátku kapitoly o balíčcích jsme si říkali, že prvním příkazem zdrojového kódu třídy, která není v kořenovém, nepojmenovaném balíčku, musí být příkaz package. O to se při použití BlueJ naštěstí nemusíte starat, protože BlueJ při oteví-rání okna daného balíčku zkontroluje příkazy package všech tříd v balíčku, a po-kud v některé třídě podoba příkazu neodpovídá, tak jej ihned opraví.

Otevřete si zdrojový kód kterékoliv z tříd, které jsme zkopírovali do balíčku kap_1.tvary a přesvědčte se, že začíná příkazem package kap_1.tvary;

Pak můžete nahlédnout do balíčku kap_1.příklady, do kterého jsme přesunuli tří-du Směr a přesvědčit se, že její zdrojový kód začíná správně příkazem package kap_1.příklady;

Page 386: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

386 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 386 z 433

9.5 Jmenné prostory a příkaz import Vraťte se nyní do balíčku kap_1.tvary a požádejte o překlad jeho tříd. BlueJ přeloží všechny třídy s výjimkou třídy Trojúhelník, u které prohlásí

cannot resolve symbol – class Směr

a zvýrazní přitom řádek, na kterém je definovaná implicitní směr. Překladač je totiž ochoten použít v definici pouze třídy, které jsou ve stejném balíčku a bude-te-li chtít použít třídu z jiného balíčku, musíte mu to výslovně povědět a nebo po-užít plný název dané třídy, který sestává z názvu balíčku následovaného tečkou a názvem třídy.

Naše třída Směr se po svém přestěhování jmenuje kap_1.příklady.Směr. Zkus-te proto její název opravit a upravte deklaraci konstanty do tvaru: public static final kap_1.příklady.Směr IMPLICITNÍ_SMĚR = kap_1.příklady.Směr.SEVER;

Zkuste nyní třídu znovu přeložit. Překladač tentokrát deklaraci konstanty akcep-tuje, ale zarazí se o kousek dál na řádku, na němž je deklarován atribut směr. Moh-li bychom sice upravit definici názvu třídy i zde, ale to by nestačilo, protože se zdrojový kódu odkazuje na tuto třídu vícekrát.

Použití plného názvu třídy je vhodné opravdu pouze tehdy, vyskytuje-li se třída ve zdrojovém kódu jednou či dvakrát. Jakmile se v programu vyskytuje čas-těji, je vhodnější jiný postup. Při něm překladači na začátku zdrojového souboru řekneme, že budeme chtít danou třídu používat, a v dalším programu pak již mů-žeme používat její stručný název bez nutnosti uvedení názvu balíčku.

K deklaraci toho, že se chystáme používat třídu z jiného balíčku, slouží pří-kaz import, v němž je toto klíčové slovo následováno plným názvem třídy. V na-šem příkladu bychom tedy vložili za příkaz package příkaz: import kap_1.příklady.Směr;

Tento příkaz musí být ale na počátku zdrojového souboru. Před ním smí být pou-ze příkaz package.

Vložte tento příkaz import na počátek zdrojového souboru, odstraňte z dekla-race konstanty IMPLICITNÍ_SMĚR plný název třídy (to sice není nutné, ale kód pak lépe vypadá) a požádejte znovu o překlad. Tentokrát již bude překladač spokojen a třídu přeloží.

Import více tříd Zkusíme nyní něco maličko složitějšího. Otevřete balíček kap_1.příklady a příka-zem Úpravy → Nová třída ze souboru do něj přidejte první verzi třídy Strom z třetí kapi-toly, tj. třídu Strom_3a z projektu 03_Třídy_Z.

Page 387: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 387

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 387 z 433

Pokusíte-li se nyní tuto třídu přeložit, překladač se podle očekávání vzbouří, protože nebude znát třídu Elipsa. Protože dopředu víme, že nebude znát ani třídy Barva, Obdélník a Plátno, potřebovali bychom deklarovat import každé z nich.

V jednom příkazu import můžeme deklarovat pouze jednu třídu. Budeme-li ctít importovat několik tříd, musíme použít několik příkazů import. V našem pří-padě by tedy mohl mít počátek třídy tvar: 1 2 3 4 5 6 7 8 9

package kap_1.příklady; import kap_1.tvary.Barva; import kap_1.tvary.Elipsa; import kap_1.tvary.Obdélník; import kap_1.tvary.Plátno; //============================================================================== //Zbytek zdrojového souboru

Zkuste nyní třídu přeložit a pokud jste neudělali žádnou chybu, měl by překlad proběhnout bez chyby.

Situace, kdy potřebujeme dovézt větší množství tříd z jednoho balíčku je po-měrně častá. Proto Java nabízí zkrácenou podobu zápisu, při níž místo názvu im-portované třídy uvedeme hvězdičku. Tím překladači oznamujeme, že z daného balíčku dovážíme všechny třídy.

V našem případě bychom tedy mohli spojit čtyři použité příkazy import do jediného příkazu: import kap_1.tvary.*;

Hvězdičková verze příkazu import je sice stručná, ale není příliš doporučovaná, protože z ní není na první pohled poznat, které ze tříd importujete a které ne. Po-užívejte ji proto opravdu pouze tehdy, budete-li z daného balíčku importovat převážnou většinu jeho tříd.

Podbalíčky Při práci s balíčky musíte mít neustále na paměti, že podbalíček není součástí ba-líčku. Budu-li proto někdy tvrdit, že cosi platí pro všechny třídy v balíčku, bude to platit opravdu jenom pro ně a nesmíte si dané tvrzení přenášet na třídy v jejich podbalíčcích.

Importujete-li proto pomocí hvězdičkové konvence všechny názvy tříd z da-ného balíčku, neimportujete tím ještě názvy tříd z jeho podbalíčků. Budete-li je chtít používat, musíte je importovat zvlášť. Znovu ale opakuji, že používání

Page 388: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

388 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 388 z 433

hvězdičkového tvaru se považuje za méně čisté a některá prostředí dokonce vy-dávají v takovém případě varovné zprávy.

Balíček java.lang Zvláštní postavení mezi všemi balíčky má balíček java.lang. V tomto balíčku jsou základní třídy, které jsou používané ve všech programech. Proto je tento balíček dovezen vždy implicitně, takže se již nemusí explicitně dovážet prostřednictvím příkazu import.

My jsme prozatím veřejně používali z tohoto balíčku jedinou třídu, a to třídu java.lang.String. Brzy však dojde i na další.

Nevýhody koncepce balíčků v BlueJ Používání balíčků je zejména u složitějších nutné, protože je výrazně zpřehlední a sníží tak pravděpodobnost chyb. Pro uživatele BlueJ však má jednu nevýhodu: přijdou o možnost definovat implementaci rozhraní třídou prostým natažením šipky. BlueJ totiž neumí natáhnout šipku mezi dvěma projekty a tím ani mezi dvěma balíčky. Budeme-li proto potřebovat dědit třídu nebo implementovat roz-hraní z jiného balíčku, nezbude nám, než napsat příslušnou klauzuli do hlavičky třídy ručně.

Stejně, jako v BlueJ přicházíme o možnost jednoduché definice dědičnosti tříd či implementace rozhraní, přicházíme někdy i o možnost jednoduchého získávání odkazů na instance. BlueJ vám totiž neumožňuje definovat třídu v jednom balíčku (projektu), zavolat její konstruktor nebo metodu, která vrací nějaký odkaz, a takto získaný odkaz umístit do zásobníku odkazů jiného balíčku (projektu)

Přemosťovací třída Zdá-li se vám, že budete při práci v některém balíčku budete častěji potřebovat volat konstruktory a metody tříd z jiných balíčků přímo z BlueJ, mohlo by po vás být výhodné vytvořit přemosťovací třídu, ve které definujete metody třídy, které budou potřebné konstruktory a statické metody tříd z jiných balíčků volat za vás.

Abych vám tuto ideu trochu naznačil, ukážu vám kód takové jednoduché přemosťovací třídy, kterou si můžete vytvořit a vyzkoušet v balíčku kap_1.příklady: package kap_1.příklady;

Page 389: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 389

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 389 z 433

import kap_1.tvary.AktivníPlátno; import kap_1.tvary.Barva; public class Most { public static Plátno getPlátno() { return Plátno.getPlátno(); } public static Barva getBarva( String název ) { return Barva.getBarva( název ); } }

Přemosťovací třída se hodí tehdy, chcete-li s třídami a jejich instancemi ručně ex-perimentovat a potřebujete-li k tomu přístup ke konstruktorům a/nebo metodám v jiných balíčcích. Nehodláte-li si však hrát s programem přímo v prostředí BlueJ, ale chcete-li jej např. pouze otestovat, žádnou přemosťovací třídu nepotřebujete, protože veškerý potřebný kód pro volání metod z jiných balíčků můžete přímo zapsat do metod testovací třídy.

9.6 Přístupová práva v rámci balíčku Výhodou umístění všech souvisejících tříd do jednoho balíčku není pouze pře-hlednost programu. Třídy, které jsou spolu uvnitř jednoho balíčku mají totiž vůči sobě větší práva, než třídy, které jsou každá v jiném balíčku. Můžeme to chápat tak, že třídy v balíčku spolu kamarádí a proto si vzájemně i více důvěřují. Jsou proto ochotny umožnit svým „kamarádům“ přístup k některým složkám (atribu-tům a metodám), k nimž třídy z jiných balíčků nepustí.

Složky, k nimž je třída ochotna pustit své „kamarády“ nemají žádný modifi-kátor přístupu. Tento implicitní modifikátor přístupu je tedy vedle modifikátorů public, protected a private čtvrtou verzí specifikace přístupových práv (víc jich Java nemá). V hierarchii přísnosti vystupuje jako druhý nejpřísnější.

Pro implicitní přístupová práva se používá bud výstižnější termín pack-age private (soukromé v balíčku) a nebo méně výstižný (avšak v textu lépe použitelný) termín friendly – přátelské (přátelům toho dovolíme víc než ostatním).

Page 390: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

390 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 390 z 433

Zopakujme si nyní celou hierarchii přístupových práv:

private povoluje přístup pouze kódu dané třídy,

package private jako oprivate, ale nevíc povoluje přístup i kódu tříd ve stejném ba-líčku,

protected jako package private, ale navíc povoluje přístup i kódu dceřiných tříd, byť jsou z jiných balíčků,

public povoluje přístup úplně každému.

Použití přátelských složek usnadňuje tvorbu programů. Umožňuje totiž třídám sdílet v rámci balíčku některé pomocné metody a proměnné, o kterých nemusí ostatní třídy vůbec vědět. Této možnosti byste však neměli zneužívat. Každé ta-kovéto sdílení totiž nabourává zapouzdření jednotlivých tříd a přináší tak riziko, že při některé z budoucích úprav zapomenete na nějakou důležitou závislost a zanesete do programu chybu.

V naší knihovně tvarů by např. nebylo nejmoudřejší, kdyby třída Plátno zpřístupnila ostatním třídám svůj atribut jedináček, v němž uchovává odkaz na používané plátno. Třídy by sice nemuseli pokaždé o tento odkaz žádat, avšak při jakékoliv budoucí úpravě třídy Plátno bychom se vystavovali riziku, že změnu chování tohoto atributu zapomeneme přenést do všech tříd, které jej používají.

Dohodněme se proto, že ne-konstantní atributy budeme dále deklarovat jako soukromé a jako přátelské budeme deklarovat pouze pomocné konstanty a meto-dy, které mají své omezené použití pouze v rámci svého balíčku.

9.7 Tvorba vlastních aplikací V podkapitole Vytvoření samostatné aplikace na straně 168 jsme si říkali, jak je mož-no uložit vytvořenou aplikaci tak, aby ji pak bylo možno spouštět obdobně, jako jiné programy. Připomenu jenom, že jsem si tehdy říkali, že Java ukládá aplikace do souborů s příponou JAR, což jsou obyčejné ZIP soubory obsahující vedle sou-borů aplikace ještě soubory s pomocnými informacemi.

Při tvorbě aplikací využívajících balíčky je třeba mít na paměti, že BlueJ zabalí do JAR souboru veškerý obsah stromu balíčků. Bude-li proto součástí aplikace pouze část stromu balíčků, musíme tuto část nejprve zkopírovat do nové, prázdné složky a teprve tuto složku exportovat do soubory JAR. Nesmíme přitom zapo-menout zkopírovat příslušné složky včetně jejich rodičovských složek a do koře-nové složky. Nejlépe si vše ověříte tak, že celou aplikaci po zkopírování přeložíte a zkusíte spustit.

Page 391: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 9: Budete si to přát zabalit? 391

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 391 z 433

9.8 Knihovny Občas se stane, že nepotřebujete uložit celou aplikaci, ale hodilo by se vám uložit pouze nějaký balíček nebo skupinu balíčků, které ve svých projektech používáte.

9.9 Shrnutí – co jsme se naučili V této kapitole jsme se seznámili s balíčky, jejich funkcí a použitím. Nyní byste již měli vědět, že:

Do jednoho balíčku patří třídy, jejichž zdrojové, resp. přeložené soubory jsou ve stejné složce.

Balíčky tvoří hierarchickou strukturu, která odpovídá struktuře složek.

Každé vývojové prostředí definuje vlastní způsob jak označit kořenovou složku stromu balíčků.

Název balíčku sestává z vlastního názvu balíčku, kterému předchází název rodičovského balíčku oddělený tečkou.

Vlastní název balíčku je shodný s názvem složky obsahující třídy patřící do tohoto balíčku. Název složky proto musí odpovídat pravidlům pro tvorbu identifikátorů.

Podle konvence se pro názvy balíčků používají pouze malá písmena.

Příslušnost třídy k balíčku musíme definovat v příkazu package.

Příkaz package musí být úplně prvním příkazem v souboru před nímž smějí předcházet pouze komentáře.

Úplný název třídy je dán názvem balíčku následovaným tečkou a vlastním názvem třídy.

Chceme-li pracovat s třídami z jiných balíčků a musíme buď používat jejich úplné názvy, nebo musíme jejich názvy nejprve dovézt pomocí příkazu im-port.

Před příkazem import smí předcházet pouze příkaz package nebo jiný příkaz import.

Příkaz import sestává z klíčového slova import následovaného úplným ná-zvem dovážené třídy a středníkem.

Page 392: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

392 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 392 z 433

Nahradíme-li název třídy (tj. část úplného názvu za poslední tečkou) hvěz-dičkou, dovezeme názvy všech tříd z uvedeného balíčku.

Podbalíček nepatří do balíčku. Dovezením všech názvů tříd v daném balíčku ještě nedovážíme třídy jeho podbalíčků – ty potřebují vlastní příkaz import.

Jediné dva balíčky, které není třeba importovat, jsou systémový balíček java.lang a implicitní, bezejmenný balíček odpovídající kořenové složce stromu balíčků.

BlueJ se k balíčkům (přesněji ke stromům balíčků) chová jako k samostatným projektům.

Pro BlueJ je kořenovým balíčkem balíček, jehož složka obsahuje soubor bluej.pkg, avšak její rodičovská složka jej již neobsahuje.

Implementace rozhraní z jiného balíčku není v BlueJ možno definovat nata-žením příslušné šipky, ale musí se zadat do zdrojového kódu „ručně“.

V projektu není možno přímo vytvořit instance tříd z jiných balíčků, tj. vy-tvořit je zavoláním jejich konstruktoru. Tyto instance lze vytvořit např. po-mocí speciální přemosťovací třídy se sadou statických metod zprostředkujících volání příslušných konstruktorů a statických metod.

Neuvedeme-li u metody či atributu žádný modifikátor přístupu, bude tato metoda (atribut) považována za soukromou v rámci balíčku, tj. budou k ní mít přístup pouze třídy z daného balíčku.

Nové termíny

Page 393: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 10: Knihovny 393

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 393 z 433

10. Knihovny

Kapitola 10 Knihovny

☯ Co se v kapitole naučíme Tuto kapitolu bychom mohli označit za opakovací. Měli bychom si vy-řešit pár úloh, na nichž si především zopakujeme, co jsme doposud pro-brali.

10.1 Pracujeme s náhodou Příklad: Brownův pohyb molekul

10.2 Návrhový vzor Adaptér Příklad: robot

Page 394: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

394 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 394 z 433

10.3 Pracujeme s obrázky

10.4 Přetypovávání na rodiče a na potomka

Na počátku kapitoly jsem říkal, že potomek je speciálním případem rodiče a může se proto v programu kdykoliv vydávat za instanci svého rodiče.

10.5 Shrnutí – co jsme se naučili Teze

Nově zavedené termíny Termín

Page 395: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 395 z 433

Část 3: Přemýšlíme

Page 396: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

396 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 396 z 433

11. Program začíná přemýšlet

Kapitola 11 Program začíná přemýšlet

☯ Co se v kapitole naučíme Text

Příklady:

Definujme třídu UFORot, jejíž instance se po opuštění vesmíru na jedné straně se vrátí na druhé straně.

Definujme třídy robotů, kteří soutěží o vysbírání co největšího počtu značek.

Definujeme ovladač klávesnice - Piškorky

11.1 Jednoduché rozhodování Naše dosavadní programy prováděly akci za akcí a nijak nepřemýšlely nad dů-sledky svého počínání. Většina úloh byla sice navržena tak, aby instance nemuse-ly přemýšlet nad tím, co dělají, ale někdy by nás trocha toho přemýšlení potěšila.

Jako příklad aplikace, kde by nám naše instance mohly trochu vyjít vstříc, by mohla sloužit naše z klávesnice ovládaná UFO. Pokud jsme včas a správně neza-reagovali, UFO odletělo mimo viditelnou část vesmíru a jen velice těžko se nám podařilo je dostat zpět.

Pokusme se tuto nepříjemnou vlastnost našich UFO trochu zmírnit. Definuj-me novou třídu, jejíž instance se budou pohybovat ve vesmíru, který je „navinut na kouli“. Jakmile UFO opustí naše zorné pole v jednom směru, objeví se vzápětí na opačném konci okna. UFO se nám tak přestanou ztrácet, protože vždy, když nám utečou, tak se vzápětí objeví někde jinde.

Page 397: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 11: Program začíná přemýšlet 397

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 397 z 433

Aby se mohlo UFO takto chovat, musí umět po každém posunu podívat na svoji pozici a podle ní se rozhodnout o svém případném přemístění za opačný okraj okna vesmíru. K takovémuto rozhodování slouží v Javě příkaz if. Tento příkaz sestává z klíčového slova if následovaného závorkami s testovanou pod-mínkou. Za závorkami pak následuje příkaz, který se má provést v případě, že je podmínka splněna. Není-li splněna, příkaz se jednoduše přeskočí.

Ukažme si vše na naší hře s parkováním UFO. Vytvořme třídu RotUFO (rotují-cí UFO), která bude dceřinou třídou naší třídy UFO. Její instance se budou chovat naprosto stejně jako instance rodičovské třídy s jediným rozdílem: utečou-li z ok-na zobrazujícího viditelnou část vesmíru, objeví se vzápětí na jeho opačném konci.

Přesouvání instancí má na starosti metoda popojeď(int). Pro splnění našeho úkolu nám stačí tuto metodu překrýt. Vše ostatní může zůstat při starém. Jediná další věc, kterou budeme muset definovat, je konstruktor a i ten by mohl přene-chat všechny starosti o vytvoření instance konstruktoru rodičovské třídy.

Zamysleme se nad tím, jak by měla překrývající verze metody popojeď(int) vypadat. Na počátku by mohla zavolat rodičovskou verzi metody, která nastaví nové hodnoty rychlosti a pozice instance. Pak se podívá, jestli instance neutekla z okna a pokud ano, změní její pozici tak, aby se po příštím přesunu objevila na opačném konci okna.

K tomu, aby metoda poznala, jestli instance opustila okno vpravo nebo dole, potřebuje znát šířku a výšku okna vesmíru. Na to se musí zeptat instance vesmí-ru. Protože ale tato velikost zůstává neměnná po celou dobu běhu aplikace, mů-žeme ji deklarovat jako konstantu třídy.

Nebudeme instanci přesouvat hned, jak se dotkne okraje okna, ale počkáme až za okrajem bezpečně zmizí. Můžeme se např. rozhodnout, že ji přesuneme za opačný konec okna až poté, co se přesune za okraj okna o větší vzdálenost, než je velikost talíře. Abychom se nemuseli pořád ptát talíře na jeho rozměr, můžeme i tuto hodnotu definovat jako konstantu.

Výsledná podoba definice třídy by mohla vypadat následovně: package rup.česky._10_přemýšlí; import rup.česky._03_třídy.ufo.*; public class RotUFO extends UFO { //== KONSTANTNÍ ATRIBUTY TŘÍDY ================================================= private final Vesmír V = Vesmír.getVesmír(); protected final int VŠÍŘKA = V.getŠířka(); protected final int VVÝŠKA = V.getVýška(); protected static final int TV = Talíř.VELIKOST;

Page 398: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

398 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 398 z 433

//== KONSTRUKTORY A TOVÁRNÍ METODY ============================================= public RotUFO (Talíř talíř, int pořadí) { super( talíř, pořadí ); } //== PŘEKRYTÉ KONKRÉTNÍ METODY RODIČOVSKÉ TŘÍDY ================================ public void popojeď( int frekvence ) { super.popojeď( frekvence ); if( xPos < -TV ) xPos = VŠÍŘKA + TV; if( xPos > (VŠÍŘKA + TV) ) xPos = -TV; if( yPos < -TV ) yPos = VVÝŠKA + TV; if( yPos > (VVÝŠKA + TV) ) yPos = -TV; talíř.setPozice( xPos, yPos ); číslo.setPozice( xPos, yPos ); } }//public class RotUFO extends UFO

Budete-li chtít definici této tříd vyzkoušet, musíte dispečerovi sdělit, že místo tří-dy UFO, kterou má přednastavenu jako implicitní, bude používat vaši třídu. To mu sdělíte tak, že zavoláte jeho metodu setUFOClass(Class), které předáte jako parametr objekt vlastní třídy.

Třídy jako objekty Jestli si dobře vzpomínáte, tak jsem na počátku učebnice říkal, že v objekto-vém programování je všechno objekt. Když všechno, tak i třída. Objekty třídjsou instancemi třídy Class. Chcete-li získat odkaz na instanci objektu před-stavujícího třídu, máte několik možností:

Nejjednodušší z nich je napsat jméno třídy následované tečkou a klíčo-vým slovem class tak, jak jste to mohli vidět v předchozím programu.

Druhou možností je zavolat metodu instance getClass(). Tuto metodu dědí všechny třídy od třídy Object, takže na ni musí každá instance umět reagovat.

Třetí možností je použití metody forName(String), která je statickou me-todou třídy Class a očekává jako svůj parametr úplné jméno třídy (tj.včetně balíčku), po jejímž objektu se ptáte.

Page 399: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 11: Program začíná přemýšlet 399

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 399 z 433

V našem předchozím programu jsme potřebovali provést po každémtestu opravdu pouze jeden příkaz. Takováto situace je však spíše výji-mečná. Většinou je potřeba provést příkazů více. V takovém případě sevšechny tyto příkazy uzavřou mezi složené závorky. Tím vytvoří slo-žený příkaz, který překladač chápe jako příkaz jediný.

Složené závorky a jimi vytvořený složený příkaz můžeme samo-zřejmě použít i tehdy, když se má provést jediný příkaz (překladač po-čet příkazů uvnitř složených závorek nekontroluje). Kdybychom jepoužili, mohl by první podmíněný příkaz z předchozího programu vy-padat např. následovně: if( xPos < -TV ) { xPos = VŠÍŘKA + TV; }

11.2 Výběr ze dvou možností

11.3 Když – jinak

11.4 Násobný výběr,

11.5 Přepínač

11.6 Shrnutí – co jsme se naučili Teze

Page 400: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

400 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 400 z 433

Nově zavedené termíny Termín

Page 401: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 12: Ještě jednu rundu, prosím 401

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 401 z 433

12. Ještě jednu rundu, prosím

Kapitola 12 Ještě jednu rundu, prosím

☯ Co se v kapitole naučíme V této kapitole.

12.1 Podkapitola

12.2 Shrnutí – co jsme se naučili

Nové termíny

Page 402: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

402 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 402 z 433

13. Kontejnery nejsou jen na odpadky

Kapitola 13 Kontejnery nejsou jen na odpadky

☯ Co se v kapitole naučíme Text

13.1 Podkapitola Text

13.2 Shrnutí – co jsme se naučili Teze

Nově zavedené termíny Termín

Page 403: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 14: Pole 403

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 403 z 433

14. Pole

Kapitola 14 Pole

☯ Co se v kapitole naučíme Text

14.1 Podkapitola Text

14.2 Shrnutí – co jsme se naučili Teze

Nově zavedené termíny Termín

Page 404: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

404 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 404 z 433

15. Jak nebýt výjimečný

Kapitola 15 Jak nebýt výjimečný

☯ Co se v kapitole naučíme Text

15.1 Podkapitola Text

15.2 Shrnutí – co jsme se naučili Teze

Nově zavedené termíny Termín

Page 405: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Kapitola 16: Co jsme si ještě neřekli 405

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 405 z 433

16. Co jsme si ještě neřekli

Kapitola 16 Co jsme si ještě neřekli

☯ Co se v kapitole naučíme Text

16.1 Podkapitola Text

16.2 Shrnutí – co jsme se naučili Teze

Nově zavedené termíny Termín

Page 406: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 406 z 433

Část 4: Přílohy

Page 407: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 407 z 433

A Instalace vývojového prostředí

Příloha A Instalace vývojového prostředí

A.1 Instalace Java 2 SDK Vývojové prostředí pracuje na platformě Java. Abyste je mohli používat, musíte mít nejprve tuto platformu nainstalovánu. Protože je to vývojové prostředí a ne obyčejný program, požaduje instalaci takzvaného JDK (Java Development Kit – vývojová sada Java), což je platforma doplněná o základní vývojové nástroje (pře-kladač, ladící program, generátor dokumentace a další). JDK si můžete zdarma stáhnout na stránkách firmy Sun na adrese http://java.sun.com.

Nespleťte si JDK s JRE (Java Runtime Environment), které sice obsahuje kompletní balík programů potřebný pro spouštění aplikací v Javě, ale chybí mu právě takové drobnosti, jako překladač nebo generátor do-kumentace, bez kterých nic nevyvinete.

Současně dejte si pozor na to, abyste stáhli standardní edici označo-vanou J2SE. Vedle ní zde totiž najdete ještě J2ME určenou pro vývojaplikací pro mobilní telefony a J2EE určenou pro vývoj distribuovaných aplikací, které jsou schopny běžet na několika počítačích komunikují-cích po síti.

Na stránce si můžete vybrat mezi verzemi pro Windows a Linux. Počítejte však s tím, že stahovaný soubor má přibližně 50 MB (hovořím o verzi 1.4.2_04 – budete-li stahovat mladší verzi, bude určitě větší) a po rozbalení bude mít asi dvakrát tolik.

Umíte-li anglicky a máte-li rozumně rychlé připojení, doporučuji vám navíc stáhnout na téže stránce i dokumentaci, avšak znovu vás musím připravit na to,

Page 408: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

408 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 408 z 433

že stahovaný soubor má přes 30 MB po rozbalení bude samotná dokumentace za-bírat na disku okolo 180 MB.

Máte-li proto někde ve svém okolí někoho, kdo v tomto jazyku programuje (může to být i firma), bude možná výhodnější požádat jej o vypálené CD. Při té příležitosti vám může vypálit i nějaké vývojové prostředí, ve kterém budete pra-covat poté, co BlueJ opustíte. JDK spolu s různými vývojovými prostředími se jednou za čas objeví také na CD, která jsou přibalena k některým počítačovým ča-sopisům.

Pracujete-li na počítači, kde je Java již instalovaná, ověřte si, že je na něm opravdu instalované JDK (někdy bývá označováno také jako SDK – Software De-velopment Kit) verze 1.4 nebo novější.

Budete-li v budoucnu pracovat s Javou, bude množství stažených a instalo-vaných programů rychle narůstat. Můžete mít např. instalovány různé verze JDK (jednu pro desktopové aplikace, druhou pro vývoj aplikací pro mobilní telefony, třetí pro vývoj aplikací pracujících na několika počítačích současně), různá vývo-jová prostředí (já vedle BlueJ, které používám v této učebnici, používám při své práci ještě další tři) a řadu nejrůznějších užitečných pomocných programů.

Já jsem to na svém počítači vyřešil tak, že věci týkající se Javy neinstaluji do složky Program fines, kam se ve Windows programy standardně umisťují, ani do ko-řené složky, kam se řada z nich vnucuje (jsem nepřítelem zaplácané kořenové složky), ale vytvořil jsem v kořenovém adresáři složku Java, do které všechny tyto programy instaluji. Zabíjím tím několik much jednou ranou:

Nemám programy Javy pomíchané s ostatními programy, takže je při inovaci snadno najdu (programy v Javě mívají velice krátký inovační cyklus).

Nemám problémy se staršími programy, které neměly rády mezery v ná-zvech složek.

DOTAZ: Mám tady podrobně popsat stažení a instalaci nebo je to dostatečně jasné?

Dokumentace bývá standardně umísťována do stejné složky jako zbytek JDK. V zazipovaném souboru je uspořádána do složek a podsložek, přičemž společnou rodičovská složka se jmenuje docs. Nezapomeňte, že při rozbalování se musíte to-to uspořádání dodržet. Ve složce s JDK se proto po rozbalení vedle složek bin, lib a dalších, které tam vytvořil instalační program JDK objeví ještě složka docs s řadou podložek, v nichž bude rozmístěna veškerá dokumentace.

Page 409: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Příloha A. Instalace vývojového prostředí 409

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 409 z 433

A.2 Instalace prostředí BlueJ Pro komfortní vývoj programů je užitečné používat nějaké rozumně komfortní vývojové prostředí. V této učebnici budu používat porstředí BlueJ, které navržené speciálně pro výuku programování a které si můžete zdarma stáhnout na interne-tové adrese http://www.BlueJ.org/download/download.html. Není to již taková obluda jako JDK, poslední verze 1.3.5 má pro Windows pouhých 2,5 MB.

Součástí standardní distribuce je i česká lokalizace. K tomuto prostředí je k dispozici i jednoduchý český tutoriál, který si však musíte stáhnout samostatně. Kdyby vám nestačilo to, co vám tu o prostředí, můžete si jej stáhnout např. na ad-rese http://java.pecinovsky.cz/BlueJ/BlueJ_Tutorial_Czech_RUP.pdf.

Prostředí BlueJ budeme instalovat dvoufázově: v první etapě si nainstalujete vlastní prostředí a ve druhé pak instalaci doplníte o speciální rozšíření, které vlastnosti tohoto prostředí přizpůsobí požadavkům této učebnice.

Instalace prostředí Instalace prostředí probíhá v postatě standardně. Instalační program se vás nej-prve zeptá, zda chcete opravdu instalovat BlueJ a odpovíte-li, že ano, spustí prů-vodce instalací, který vás nejprve přivítá a pak vás postupně nechá zadat složku, do níž chcete BlueJ instalovat (doporučuji vám použít podobnou účelovou složku, jako je moje složka Java, o níž jsem vám vyprávěl při instalaci JDK), skupiny star-tovací nabídky do níž chcete umístit jeho zástupce a zeptá se vás, zda chcete umís-ti ikonu BlueJ na pracovní ploše. Předpokládám proto, že nepotřebuje nijaký podrobný výklad.

Instalace rozšíření Když budete mít BlueJ instalováno, měli byste si instalovat ještě rozšíření, které si můžete stáhnout z adresy http://vyuka.pecinovsky.cz. Toto rozšíření je v zazipo-vaném souboru, který je třeba rozbalit do podsložky lib složky, do níž jste instalo-vali BlueJ.

Máte-li tedy stejně jako já BlueJ instalovaný ve složce C:\Java\BlueJ, budete roz-šíření rozbalovat do složky C:\Java\BlueJ\lib.

Rozšíření nahradí jeden programový soubor jeho opravenou verzí, aktualizu-je dva konfigurační soubory a přidá složku rup, která obsahuje českou lokalizaci a některé soubory, které budeme v učebnici využívat.

Page 410: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

410 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 410 z 433

První spuštění Po prvním spuštění program otevře dialogové okno BlueJ Launcher, v němž se vás zeptá na umístění JDK. V tomto dialogovém okně vypíše všechna JDK, která na vašem počítači identifikuje. Nalézá-li se mezi nimi to, s nímž chcete pracovat, klepněte na příslušný řádek a pak stiskněte tlačítko Launch BlueJ.

Obrázek A.1 Zadání použitého JDK

Někdy se stane, že BlueJ žádné JDK nenajde a okno bude prázdné, nebo v něm není to JDK, jež byste rádi použili. Pak stiskněte tlačítko Advanced, čímž rozbalíte spodní část okna, která nabízí další možnosti.

Obrázek 16.2 Nalezení použitého JDK

Page 411: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Příloha A. Instalace vývojového prostředí 411

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 411 z 433

Předpokládám, že víte, kam jste JDK instalovali. Pak stačí stisknout tlačítko Browse for a specific Java version… a v následně otevřeném dialogovém okně pro otevření souboru nalézt požadované JDK.

Počítejte ale s tím, že tentokrát se již BlueJ nespokojí se složkou, v níž je celé JDK instalováno (a kterou uvádí v horní části okna), ale bude chtít ukázat přímo soubor java.exe, který se nalézá v podsložce bin – při použití J2SDK 1.5.0 by to tedy na mém počítači byl soubor E:\Java\j2sdk1.5.0\bin\java.exe.

Po úspěšném zadání JDK „obživne“ tlačítko Launch BlueJ. Stiskněte je a pro-gram dialogové okno zavře, po chvíli zobrazí své zaváděcí okno a po další chvíli se BlueJ natáhne a spustí. S výše popsaným dotazem vás již příště obtěžovat ne-bude.

Pokud však své JDK později inovujete, je možno BlueJ přesvědčit, aby je začal používat. Ve startovací nabídce zadáte ve složce BlueJ příkaz Select VM, čímž znovu otevřete výše popsaná okna, která vám umožní požadovanou verzi JDK zadat.

Konfigurace BlueJ Než začnete BlueJ doopravdy používat, měli byste si jej nekonfigurovat, aby vám generoval podobné obrázky, jako ten, který používám já. Zadejte proto povel Ná-stroje → Nastavení. BlueJ otevře dialogové okno, v němž můžete zadat většinu po-třebných parametrů.

Dialogové okno má čtyři karty, z nichž se nyní podíváme pouze na první dvě.

Na kartě Editor vám doporučuji zaškrtnout všechny volby tak, jak to vidíte na obrázku. Velikost písma si nastavte tak, aby vám maximálně vyhovovala. Podle rozlišení obrazovky se bude optimální velikost pohybovat pravděpodobně mezi 10 a 12 body.

Page 412: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

412 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 412 z 433

Obrázek 16.3 Karta Editor

Na kartě URL vám taktéž doporučuji zaškrtnout všechna políčka. Do vstupního pole URL dokumentace Java API pak zadejte cestu k dokumentaci dodávanou k JDK. Pokud jste dokumentaci umístili tak, jak jsem vám radil, tak v tomto poli zadáte cestu k souboru index.html umístěném v podložce api složky docs (připomínám, že docs je složka, v níž je umístěna veškerá dokumentace).

Pokud jste dokumentaci neinstalovali, tak nic nezadávejte, ale musíte pak zrušit zaškrtnutí políčka Použít toto URL při generování projektové dokumentace.

Obrázek 16.4 Karta Různé

Page 413: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Příloha A. Instalace vývojového prostředí 413

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 413 z 433

Page 414: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

414 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 414 z 433

B Základy práce s BlueJ

Příloha B Základy práce s BlueJ

Základní vlastnosti zabudovaného editoru Autoři editoru dodávaného s prostředím BlueJ se snažili vytvořit program maxi-málně jednoduchý, aby žáci zvládli jeho ovládání co nejrychleji, avšak na druhou stranu dostatečně komfortní, aby jim tvorbu jejich programů co nejvíce usnadnil.

Mezi základní „komfortní doplňky“, které v současné době patří již k téměř povinné výbavě všech programátorských editorů, patří možnost automatického odsazování, barevného zvýraznění syntaxe, vytváření záložních kopií, zobrazo-vání čísel řádků a schopnost dohledání a označení párové závorky k závorce, u které je textový kurzor. Navíc nám zabudovaný editor umožňuje nastavit velikost použitého písma (jinou použijeme při vlastní tvorbě a jinou pak při její prezentaci před skupinou posluchačů).

Všechny tyto vlastnosti můžeme nastavit či naopak potlačit v dialogovém okně, jež vyvoláme příkazem Předvolby → Nastavení….

Obrázek Chyba! V dokumentu není žádný text v zadaném stylu..5: Nastavení předvoleb pro editor a překladač.

Když už jsme toto dialogové okno otevřeli, povíme si i o ostatních možnostech, které jdou na kartě Různé nastavit. Prvním z nich je povolení novinek z JDK 1.4. Verze 1.4 totiž zavedla nové klíčové slovo assert. Jeho použití je však implicitně zakázáno pro případ, že by je někdo ve svých starších programech již použil jako identifikátor. Kdo však ví, že slovo assert ve svých programech nikdy nepoužil, může je povolit. Protože my vytváříme programy zcela nové, rádi jeho použití povolíme, abychom později mohli využít jeho příjemných vlastností.

Page 415: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Příloha B. Základy práce s BlueJ 415

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 415 z 433

Druhou možností je nastavení adresy s dokumentací standardní knihovny. Po instalaci je zde nastavena internetová adresa této dokumentace na stránkách firmy Sun. Pokud jste spolu s JDK instalovali i jeho dokumentaci, doporučuji vám přepsat tuto adresu na lokální adresu ve vašem počítači. Na obrázku Chyba! V dokumentu není žádný text v zadaném stylu..5 si všimněte, že se zde nastavuje absolutní cesta k souboru index.html ve složce api, jež je podložkou složky, do níž jste dokumentaci instalovali.

Pokud jste se zalekli rozměru dokumentace a neinstalovali jste ji, zrušte za-škrtnutí políčka Použít toto URL při generování projektové dokumentace. Jedinou výhodou, o níž se zrušením tohoto zaškrtnutí připravíte, je to, že do vaši dokumentace nebu-dou vloženy odkazy na dokumentaci souvisejících částí standardní knihovny. Až si budeme povídat o dokumentaci, ještě se k tomu vrátím.

Page 416: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

416 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 416 z 433

C Syntaktické diagramy

Příloha C Syntaktické diagramy

Page 417: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Příloha D. Použité projekty 417

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 417 z 433

D Použité projekty

Příloha D Použité projekty Všechny dále popisované projekty jsou připraveny ke stažení na internetové adre-se http://vyuka.pecinovsky.cz. Odtud byste si je měli všechny stáhnout před tím, než se pustíte do studia.

02_Objekty Úvodní projekt, v němž se čtenář seznámí s pojmy třída a objekt a naučí se inter-aktivně vytvářet instance, volat metody tříd a instancí a prohlížet si jejich atributy.

03_Třídy_A Výchozí verze projektu, na kterém se učíme psát vlastní definice tříd. Tento pro-jekt obsahuje stejné třídy jako předchozí projekt. Změna je v jejich upraveném uspořádání snažícím se o lepší sledovatelnost čar závislostí a v přidání pomocné třídy P obsahující některé užitečné metody.

03_Třídy_Z Závěrečná verze projektu obsahující oproti projektu 03_Třídy_A všechny třídy, které jsme vytvořili v průběhu 3. a 4. kapitoly.

03_UFO Závěrečný projekt třetí kapitoly v němž má čtenář za úkol naprogramovat třídu UFO, která bude součástí rozsáhlejší aplikace realizující jednoduchou hru. V této hře má hráč za úkol dovést jednotlivé instance třídy UFO ze startovací rampy do hangárů.

Page 418: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

418 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 418 z 433

04_Zlomky Závěrečný projekt čtvrté kapitoly, v němž má čtenář za úkol vytvořit třídu Zlomek umožňující zlomkovou aritmetiku. Projekt obsahuje i předpřipravené vzorové ře-šení a testovací programy pro vzorovou i uživatelem vytvořenou třídu.

05_Vzory Projekt navazuje na projekt 03_Třídy_Z. Doplňuje jej o definici tří přepravek a upra-vuje definice tříd tak, aby mohli přepravek využít. Navíc obsahuje třídu definující jedináčka a třídy definující výčtové typy.

06_Rozhraní_A Úvodní projekt šesté kapitoly přináší nově koncipovanou sadu základních tříd geometrických tvarů, jejíž hlavní změnou je výměna třída Plátno za třídu AktivníPlátno a z ní vyplývající úpravy ostatních tříd.

06_Rozhraní_Z Projekt, který je rozšířením předchozího projektu o všechny třídy, které jsme v průběhu šesté kapitoly zavedli nebo definovali.

07_Dědění_rozhraní_Z Projekt obsahující třídy ve stavu, ke kterému dospějeme na konci kapitoly a dě-dičnosti rozhraní. Zavádí dědičnost rozhraní a z toho plynoucí výhody a zavádí také třídu Multipřesouvač a rozhraní IMultiposuvný. Současně v něm najdete vyře-šenou úlohu a lanovkou a jejími kabinkami.

08_Dědičnost_tříd_pokusy Pomocný projekt sloužící při výkladu základních zákonitostí dědičnosti tříd.

08_Dědičnost_tříd_A Výchozí projekt kapitoly pojednávající o dědičnosti tříd. Obsahuje třídy grafic-kých objektů s atributy AP definovanými jako protected, avšak prozatím bez spo-lečné rodičovské třídy.

Page 419: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

Příloha D. Použité projekty 419

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 419 z 433

08_Dědičnost_tříd_B Předchozí projekt rozšířený o třídy, které vytvoříme do chvíle, než definujeme společnou rodičovskou třídu pro všechny grafické třídy.

08_Dědičnost_tříd_C Předchozí projekt po definici společné rodičovské třídy pro všechny třídy imple-mentující rozhraní IPosuvný.

08_Dědičnost_tříd_D Předchozí projekt po definici společné rodičovské třídy pro všechny třídy imple-mentující rozhraní IHýbací.

08_Dědičnost_tříd_E Předchozí projekt po převodu společných rodičovských tříd Posuvný a Hýbací na abstraktní třídy APosuvný a AHýbací.

Page 420: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 420 z 433

Rejstřík

Rejstřík

— % — %, 102, 184

— A — Ada, 20 analogie, 17, 43, 47, 57, 65,

67, 68, 72, 128, 130, 244, 265, 324, 325, 332, 333

aplikace soubor JAR, 169 spouštějící třída, 169 vytvoření, 168

argument. viz parametr arita, 182 assertEquals, 208 assertTrue, 208 atribut, 68

instance, 68 jen pro čtení, 129 kvalifikace, 134 možné důsledky

zveřejnění, 112 třídy, 68, 70, 136 veřejný

zadat jako parametr, 73 atributy

deklarace, 109

— B — Babbage, 19 běhová chyba, 95, 97 bílý znak, 94 BlueJ, 28

projekt otevření, 32

boolean, 146

— C — class-soubor, 86

— Č — číslo, 180

celé, 147 desetinné, 146

— D — debugger, 211

objektové atributy a proměnné, 218

pozastavení programu, 221

dědičnost, 246 definice, 81, 171

syntaktická, 113 deklarace, 81, 171 dekrementace --, 191 dělení

celočíselné (/), 183 modulo (%), 184 zbytek, 102

design pattern publikace, 229

Design Pattern, 229 diagram tříd, 33, 44

manipulace s třídami, 34 disk

substituovaný, 30 dokumentace, 149

automaticky generovaná, 160

projektu, 161 tagy. viz dokumentace –

značky značky, 164

double, 146

— E — exponent, 147

— G — gang čtyř, 229

garbage collector. viz správce paměti

GoF, 229

— H — halda, 66 historie, 19 hlavička konstruktoru, 92 hlavička třídy, 85 hodnota

návratová, 55 objektového typu, 126 odkaz, 75

vstupní, 58 výstupní, 55

— Ch — chyba

běhová, 95, 97 logická, 95, 99 sémantická. viz chyba

logická syntaktická, 95, 96

— I — IDE, 28 identifikátor

pravidla pro tvorbu, 46 implementace, 242

versus rozhraní, 128 implementace rozhraní, 250 inkrementace ++, 191 instance, 41

atribut, 68 odkaz, 56 počítání, 190 prohlížeč, 56 prohlížení, 68 rodná čísla, 190 rozhraní, 244 versus odkaz, 66 vytvoření, 44, 47, 50

Page 421: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

16. Co jsme si ještě neřekli 421

@Java_PO.doc, verze 0.33.197, uloženo: úterý 29.června.2004 – 23:55 Strana 421 z 433

zrušení, 50 int, 146 interface, 240 interpret, 23, 24

— J — JAR soubor, 169, 390 Java, 25 jazyk

hybridní, 24 programovací, 24 UML, 33

JDK, 27 Jedináček, 233 JRE, 27

— K — klíčové slovo

public, 85 kód

zdrojový, 84 komentář, 149

dokumentační, 149 obecný, 149 řádkový, 149

kompilátor. viz překladač konstanta, 143

pojmenovaná, 143 konstruktor, 43

bezparametrický, 91 definice, 91 hlavička, 61, 92 implicitní, 87 s parametry, 59, 100 this, 101

kontrakt, 129, 132 krokování programu, 212 kvalifikace, 132

implicitní, 133 this, 133

kvalifikace atributů, 134 kvalifikace metod, 132

— L — ladění, 95 literál, 143

boolean, 146 double, 146 int, 146

logická chyba, 95, 99 lokální proměnná, 139

— M — mantisa, 147 Messenger, 230 metoda, 42

assertTrue, 208 definice, 115 kvalifikace, 132 přístupová, 129

konvence pro názvy, 131

statická. viz metoda třídy, viz metoda třídy

test, 117 toString, 198 třídy, 64, 136, 137 vracející hodnotu

použití, 124 metoda assertEquals, 208 mezera

sémantická, 22 modifikátor

přístupu, 110 private, 110 protected, 329 public, 85, 110

modulární programování, 22

— N — nápověda

komentářová, 158 návratová hodnota

objektového typu, 126 návrhový vzor, 229

jedináček, 233 Služebník, 266 Stav, 298, 312 Zástupce, 304

název. viz identifikátor new, 43 null, 148, 179

— O — objekt, 41

stav, 68 objektově orientované

programování. viz OOP odkaz, 66

jako návratová hodnota, 75

zásobník odkazů, 47 OOP, 21

principy, 22 operace, 182 operační systém, 23 operand, 182 operátor, 182, 185

binární, 182 dekrementační --, 191 dělení (/), 183 inkrementační ++, 191 modulo (%), 184

násobení (*), 182 new, 43 odčítání (–), 182 přetypování, 188 přiřazení

sdružený (+=, –=, *=, /=, %=), 187

přiřazovací (=), 186 sčítání (+), 182 slučování řetězců (+). viz

String – operátor slučování

ternární, 182 unární, 182, 191 unární –, 185 unární +, 185

— P — parametr, 58, 59, 100

objektového typu, 62, 126 přímé zadání hodnoty

objektového typu, 62 primitivního typu, 59

přímé zadávání, 73 platforma, 24 private, 69, 110 program

definice, 41 interpretovaný, 24 krokování, 212 překládaný, 24 ukončení ruční, 214

programovací jazyk, 24 programování

modulární, 22 objektově orientované, 21 strukturované, 22

prohlížeč instancí, 56 projekt, 29

otevření v BlueJ, 32 umístění na disku, 29

proměnná lokální, 139

protected, 329 překlad, 34, 86 překladač, 23, 24 Přepravka, 230 přetěžování, 58, 127 přetížená verze, 58 přetypování, 188

pseudopřetypování na String, 189

přípravek, 106 úprava obsahu, 107

přiřazení, 186 sdružené, 187

přístupová metoda, 129 konvence pro názvy, 131

Page 422: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

422 Myslíme objektově v jazyku Java 1.5

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 422 z 433

public, 85, 110

— R — refaktorování, 272 rozhraní, 240

dědění několika, 291 implementace

odvolání, 265 implementace několika,

264 implementace rozhraní,

250 instance rozhraní, 244 stavové, 299, 312 versus implementace, 128

— Ř — řetězec. String

textový. viz String

— S — SDK, 27 sdružené přiřazení, 187 sémantická chyba. viz

logická chyba sémantická mezera, 22 Singleton, 233 slovo

klíčové. viz klíčové slovo Služebník, 266 soubor

JAR, 169, 390 správa paměti, 50 správce paměti, 51, 66 Stav, 298, 312 stavové rozhraní, 299, 312 stereotyp, 105 String, 147

operátor slučování (+), 183

prázdný řetězec, 179 přechod na nový řádek,

148 rozdělení dlouhého textu,

147 slučování řetězců, 177

stroj virtuální, 24, 26

restartování, 90 strukturované

programování, 22 substituovaný disk, 30 syntaktická definice, 113 syntaktická chyba, 95, 96 syntaxe, 113 systém

operační, 23

— Š — šipka

dědičnost, 246 implementační, 246 používá, 246

— T — TDD, 104, 120 terminologie, 16, 42, 51, 55,

58, 68, 71, 81, 83, 86, 90, 115, 116, 131, 133, 136, 143, 144, 148, 182, 191, 243, 246, 267, 317, 318, 325, 333, 355, 364, 365, 389

test automaticky generovaný,

206 testů, 209 úklid po testu, 208 vlastní, 207

Test Driven Development, 104

testovací třída, 105 testování, 104 textový řetězec. viz String

this konstruktor, 101 kvalifikace, 132, 133, 134

this.atribut, 132 toString, 198 třída, 41

class-soubor, 86 hlavička, 85 nová

vytvoření, 82 odstranění, 88 prázdná, 89

standardní, 199 překlad, 86 testovací, 105 uspořádání prvků v těle,

157 zdrojový kód, 84 zkopírování z disku, 177

tvar semilogaritmický, 147

typ boolean, 53 datový, 52 double, 53 hodnotový, 222 int, 53

neměnný, 222 objektový, 53

parametry, 62 vracení hodnot, 55

primitivní, 53 vracení hodnot, 54

proměnný, 222 referenční, 223 String, 54

typu referenční, 222

— U — úklid po testu, 208 úloha, 17, 49, 50, 52, 62, 64,

65, 66, 70, 73, 97, 101, 104, 109, 112, 117, 120, 122, 134, 136, 139, 141, 143, 191, 193, 199, 225, 231, 235, 261, 264, 266, 272, 280, 288, 296, 306, 338, 343, 344, 350, 351, 355, 363

UML, 33, 44

— V — velbloudí notace, 109 virtuální stroj, 24, 26

restartování, 90 VM. viz stroj virtuální vstup

jednoduchý, 176 výstup

jednoduchý, 176 standardní, 196

vzor návrhový, 229

vzor návrhový Zástupce, 304

— Z — zapouzdření, 128 zarážka, 212

zrušení, 220 zásobník odkazů, 47, 62 Zástupce, 304 zbytek po dělení, 102 zdrojový kód, 84 znak

bílý, 94 zpráva, 41

s parametry, 63 zaslání instanci, 49 žádající o hodnotu, 52

Page 423: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 423 z 433

Page 424: Myslíme objektověpub.eyim.net/ziraficka/Myslime_objektove_v_jazyku_Java.pdf · @Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 1 z 433 Myslíme objektově v

@Java_PO.doc, verze 0.33.197, uloženo: út 29.6.04 – 23:55 Strana 424 z 433

Část 5: KONEC


Recommended