Programovací jazyk Java

Podobné dokumenty
Úvod do programovacích jazyků (Java)

Příklad : String txt1 = new String( Ahoj vsichni! ); //vytvoří instanci třídy String a přiřadí ji vnitřní hodnotu Ahoj vsichni!

typová konverze typová inference

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Abstraktní třída a rozhraní

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39

4. ZÁKLADNÍ POJMY Z OBJEKTOVĚ ORIENTOVANÉHO PROGRAMOVÁNÍ

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

1. Dědičnost a polymorfismus

Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9

Výčtový typ strana 67

Generické programování

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

PŘETĚŽOVÁNÍ OPERÁTORŮ

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Teoretické minimum z PJV

Seminář Java IV p.1/38

Programování v C++ 1, 6. cvičení

7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd

8 Třídy, objekty, metody, předávání argumentů metod

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Seminář Java II p.1/43

3. Třídy. Základní pojmy objektového programování. Třídy

Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Předmluva k aktuálnímu vydání Úvod k prvnímu vydání z roku Typografické a syntaktické konvence... 20

20. Projekt Domácí mediotéka

9. přednáška - třídy, objekty

Dynamicky vázané metody. Pozdní vazba, virtuální metody

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března

Java Řetězce Java, zimní semestr

11 Diagram tříd, asociace, dědičnost, abstraktní třídy

Úvod do programovacích jazyků (Java)

Jazyk C# (seminář 3)

Algoritmizace a programování

PREPROCESOR POKRAČOVÁNÍ

Java Enum Java, zimní semestr ,2017 1

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Bridge. Známý jako. Účel. Použitelnost. Handle/Body

Dědičnost. seskupování tříd do hierarchie. potomek získá všechny vlastnosti a metody. provádí se pomocí dvojtečky za názvem třídy.

Třídy. Instance. Pokud tento program spustíme, vypíše následující. car1 má barvu Red. car2 má barvu Red. car1 má barvu Blue.

Virtuální metody - polymorfizmus

Obsah. Úvod 11 Základy programování 11 Objektový přístup 11 Procvičování 11 Zvláštní odstavce 12 Zpětná vazba od čtenářů 12 Errata 13

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

Programování v Javě I. Leden 2008

Paměť počítače. alg2 1

Třídy a dědičnost. A0B36PR1-Programování 1 Fakulta elektrotechnická České vysoké učení technické

Chování konstruktorů a destruktorů při dědění

Programování v C++ 1, 5. cvičení

Mnohotvarost (polymorfizmus)

Neměnné objekty. Tomáš Pitner, upravil Marek Šabo

Programování v Javě I. Únor 2009

NPRG031 Programování II 1 / :25:46

NMIN201 Objektově orientované programování 1 / :36:09

9. Polymorfismus a rozhraní

Jazyk C# (seminář 6)

1. Programování proti rozhraní

Dědění, polymorfismus

Abstraktní datové typy: zásobník

24. listopadu 2013, Brno Připravil: David Procházka

Principy objektově orientovaného programování

Třídy, polymorfismus. A0B36PR2-Programování 2 Fakulta elektrotechnická České vysoké učení technické

10 Balíčky, grafické znázornění tříd, základy zapozdření

7. Datové typy v Javě

Java efektivně. Lukáš Zapletal liberix.cz. Pokročilejší techniky programování v Javě

11. Dědičnost. Dědičnost strana 103

Objekty v PHP 5.x. This is an object-oriented system. If we change anything, the users object.

PB161 Programování v jazyce C++ Přednáška 7

Java Výjimky Java, zimní semestr

PB161 Programování v jazyce C++ Přednáška 7

Programování v C++ 2, 4. cvičení

Vytváření a použití knihoven tříd

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

3. Je defenzivní programování technikou skrývání implementace? Vyberte jednu z nabízených možností: Pravda Nepravda

Seznamy a iterátory. Kolekce obecně. Rozhraní kolekce. Procházení kolekcí

Java a XML. 10/26/09 1/7 Java a XML

IRAE 07/08 Přednáška č. 1

Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost rozhraním a výjimkám.

Základy objektové orientace I. Únor 2010

Programové konvence, dokumentace a ladění. Programování II 2. přednáška Alena Buchalcevová

IB111 Programování a algoritmizace. Objektově orientované programování (OOP)

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

Algoritmizace a programování

Viditelnost (práva přístupu) Tomáš Pitner, upravil Marek Šabo

Regulární výrazy. Vzory

Algoritmizace a programování

Zápis programu v jazyce C#

UJO Framework. revoluční architektura beans. verze

Datové typy strana 29

5. Ř etězce (třída String)

Čtvrtek 8. prosince. Pascal - opakování základů. Struktura programu:

OMO. 4 - Creational design patterns A. Singleton Simple Factory Factory Method Abstract Factory Prototype Builder IoC

Programovací jazyk Pascal

Programování II. Polymorfismus

Java - výjimky. private void vstup() throws IOException {... }

Konstruktory a destruktory

Více o konstruktorech a destruktorech

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

Pokročilé programování v jazyce C pro chemiky (C3220) Třídy v C++

Transkript:

1 z 9 14.1.2015 22:58 Programovací jazyk Java 3. přednáška Obsah Pořadí inicializace objektu při dědění Auto boxing/unboxing primitivních typů Řetězce Rozhraní Operátor instanceof Překrývání Překrytí metody equals Překrytí metody hashcode Finální metody a finální třídy Abstraktní metody a abstraktní třídy Polymorfismus (mnohotvarost) Vnitřní třídy Anonymní třídy Lokální vnitřní třídy Anotace kompilátoru Pořadí inicializace objektu při dědění Statické proměnné předka. Statický blok předka. Statické proměnné potomka. Statický blok potomka. Instanční proměnné předka. Konstruktor předka. Instanční proměnné potomka. Konstruktor potomka. Auto boxing/unboxing primitivních typů Přítomnost primitivních typů v jinak objektové Javě vede k potřebě jejich objektových reprezentací. Těmi jsou například pro typ int třída Integer, pro boolean třída Boolean atd. Pro různé operace s nimi jsou někdy potřeba jejich primitivní formy a jindy zase objektové reprezentace. Dříve bylo nutno převádět objektové typy na primitivní (tzv. unboxing) pomocí metody typvalue(), kde místo slova typ použijeme název odpovídajícího datového typu. Pro opačný převod z primitivního na objektový typ (tzv. boxing) bylo třeba využít konstruktoru příslušné třídy. Integer iobj = new Integer(8); int i = iobj.intvalue(); Od verze 5.0 je v jazyku Java implementován tzv. auto-boxing a auto-unboxing, takže je možné primitivní a objektové číselné typy libovolně zaměňovat. Integer iobj = 8; int i = iobj; Řetězce Řetězec je v Javě samostatný objekt, instance třídy String (přesněji java.lang.string) Tvoříme-li řetězec přímo ze znaků, nemusíme používat klíčové slovo new: String s = "Brno"; Řetězce vytvořené jako instance třídy String jsou konstantní, to znamená, že je nelze měnit. Lze sice do proměnné typu String opakovaně přiřazovat řetězce znaků, vždy ale na pozadí dojde k vytvoření nového objektu a zániku starého, což je časově (a potenciálně i paměťově) náročnější. Potřebujeme-li obsah řetězce průběžně měnit, je lepší vytvořit objekt typu StringBuffer nebo StringBuilder (viz níže). Práce s typem String je rychlejší a paměťově úspornější, takže bychom ho měli používat všude tam, kde nebude docházet ke změně řetězce. co vypíše následující program?

2 z 9 14.1.2015 22:58 String r = "A"; String s = r; r = "B"; System.out.println(s); Vytvoření řetězce Při volání konstruktoru objektu String můžeme použít tyto typy skutečných parametrů: pole bytů (byte[]), pole znaků (char[]), String, StringBuffer a StringBuilder. public class Retez1 { String s1, s2, s3, s4, s5; char[] znaky = {'E', 'v', 'a'; byte[] byty = {(byte)'j', (byte)'a', (byte)'n', (byte)'a'; StringBuffer buf = new StringBuffer("dobry den"); s1 = new String("Betalne retezec"); System.out.println("s1: " + s1); // Vypíše "s1: Betalne retezec" s2 = new String(s1); System.out.println("s2: " + s2); // Vypíše "s2: Betalne retezec" s3 = new String(znaky); System.out.println("s3: " + s3); // Vypíše "s3: Eva" s4 = new String(bajty); System.out.println("s4: " + s4); // Vypíše "s4: Jana" s5 = new String(buf); System.out.println("s5: " + s5); // Vypíše "s5: dobry den" System.out.println("Delka s5 = " + s5.length()); // Vypise Delka s5 = 9 public class Retez2 { String[] pole = {"Dana", "Eva", "Martina"; for (int i = 0; i < pole.length; i++) System.out.println(pole[i]); Inicializované pole řetězců Práce s řetězci String int compareto(string str) porovná dva řetězce Porovnání dvou řetězců int comparetoignorecase(string str) porovná dva řetězce, přičemž nerozlišuje malá a velká písmena Tyto dvě metody vrací hodnotu int číslo < 0, pokud je řetězec v parametru metody větší, 0 v případě shody obou řetězců a int číslo > 0, je-li menší. String s1 = new String("ahoj"); String s2 = new String("ahoi"); String s3 = new String("AHOJ"); System.out.println(s1.compareTo(s2)); // vypíše 1 System.out.println(s2.compareTo(s1)); // vypíše -1 System.out.println(s1.compareToIgnoreCase(s3)); // vypíše 0 boolean equals(object obj) zjistí, zda jsou řetězce shodné boolean equalsignorecase(string str) totéž bez ohledu na malá a velká písmena Tyto metody vrací true v případě shody řetězců a false v případě neshody Převod na malá či velká písmena Metody String tolowercase() a String touppercase(), obě vrací String Buď operátorem + nebo metodou String concat(string str) Spojení řetězců Náhrada všech znaků v řetězci Metoda String replace(char nahrazovany, char nahrazujici), resp. String replace(charsequence target, CharSequence replacement).

3 z 9 14.1.2015 22:58 public class Nahrada { String s2, s1 = "cacao"; s2 = s1.replace('c', 'k'); System.out.println(s1); // "cacao" System.out.println(s2); // "kakao" Práce s částí řetězce Získání části řetězce Znaky v řetězci jsou očíslovány od nuly. Metoda String substring(int beginindex) vrací podřetězec od znaku s indexem beginindex do konce řetězce. Metoda String substring(int beginindex, int endindex) vrací podřetězec od znaku s indexem beginindex do znaku před znakem s indexem endindex. Práce se začátkem a koncem řetězce Je možné snadno otestovat, jestli řetězec začíná nebo končí určitým podřetězcem. Metoda boolean startswith(string prefix) vrací hodnotu true, pokud řetězec začíná podřetězcem prefix. Metoda boolean endswith(string suffix) vrací hodnotu true, pokud řetězec končí podřetězcem suffix. Oříznutí bílých znaků na okrajích Metoda String trim() odebere všechny případné mezery, tabulátory a znaky konce řádku na začátku i na konci řetězce. Práce s jednotlivými znaky řetězce Metoda char charat(int index) vrací znak s indexem index. Získání znaku Hledání znaku Metoda int indexof(char c) vrací index prvního výskytu znaku c v řetězci. V případě nenalezení znaku v řetězci vrací hodnotu -1 (to platí i pro další vyhledávací metody). Metoda int indexof(char c, int fromindex) hledá od znaku s indexem fromindex. Metoda int lastindexof(char c) vrací index posledního výskytu znaku c v řetězci. Metoda int lastindexof(char c, int fromindex) hledá opět odzadu, tentokrát od znaku s indexem fromindex. Konverze základních datových typů na řetězec Všechny základní datové typy (6 číselných, char a boolean) lze konvertovat na řetězec metodou static String valueof(...). Jak je patrné, jedná se o metodu třídy. Stačí však také zřetězit literál nebo proměnou s prázdným řetězcem. int i = 14; double d = 3.1415927; String ri = String.valueOf(i); String rd = "" + d; Konverze řetězce na základní datové typy Pro převod se používají metody tříd Boolean, Byte, Short, Integer, Long, Float a Double z balíku java.lang. Všechny tyto třídy obsahují statickou metodu valueof(string s), která vrací řetězec zkonvertovaný na objekt třídy příslušného datového typu. Tento objekt je pak možné používat namísto primitivního typu (viz první téma této prezentace auto-unboxing) Příklad konverze: double d = Double.valueOf("3.14").doubleValue(); //explicitní unboxing boolean b = Boolean.valueOf("true"); //auto-unboxing int i = Integer.valueOf("123"); //auto-unboxing Třídy StringBuffer a StringBuilder

4 z 9 14.1.2015 22:58 Obě třídy slouží k práci s proměnlivými řetězci. Rozdíl mezi nimi je v tom, že práce s objekty třídy StringBuilder je sice rychlejší, avšak tyto objekty nejsou synchronizované (tzn. nevhodné pro současný přístup z několika vláken). Obě třídy mají stejné metody. K modifikaci uloženého řetězce slouží metody append, insert a replace. Více: viz dokumentace Java API. Rozhraní Java neumožňuje vícenásobnou dědičnost. To znamené, že každá třída může mít nejvýše jednoho předka. Jako částečnou náhradu poskytuje Java možnost použití rozhraní. Rozhraní definuje soubor metod, které v něm ale nejsou implementovány, tj. v deklaraci je pouze hlavička metody, stejně jako je to u abstraktní metody. Třída, která toto rozhraní implementuje (tj. jakoby dědí), musí implementovat (tj. jakoby překrýt) všechny jeho metody. Rozhraní je vhodné používat jako datový typ formálních parametrů metod a při deklaraci proměnných. Potom všude tam, kde je uvedeno rozhraní jako datový typ, je očekávána instance jakékoliv třídy implementující toto rozhraní. Poznámka: od Javy 8 mohou rozhraní obsahovat i výchozí (default) metody, které obsahující implementaci, ne jen hlavičku. Třída, která implementuje toto rozhraní, tak získá i tuto funkcionalitu jedná se tak v podstatě o vícenásobnou dědičnost. Zápis rozhraní se podobá zápisu třídy, např. public interface Info { public void kdojsem(); Toto rozhraní se jménem Info popisuje pouze jednu metodu kdojsem(). Konstrukce rozhraní Implementace rozhraní Třída, která implementuje rozhraní, musí uvést jeho jméno v hlavičce za klíčovým slovem implements. Při implementaci více rozhraní se uvede jejich seznam oddělený čárkou. public class Usecka implements Info { int delka; Usecka(int delka) { this.delka = delka; public void kdojsem() { System.out.println("Usecka"); public class Koule implements Info { int polomer; Koule(int polomer) { this.polomer = polomer; public void kdojsem() { System.out.println("Koule"); public class TestKoule { Usecka u = new Usecka(5); Koule k = new Koule(3); u.kdojsem(); k.kdojsem(); Použití rozhraní Když chceme třídě vnutit zcela konkrétní metody. Typicky se jedná o případ, kdy potřebujeme metodě předat referenci na objekt, který bude umět nějaké věci. Napíšeme rychle rozhraní, formální parametr metody bude typu právě tohoto rozhraní a v metodě můžeme volat metody deklarované v rozhraní. Při volání takové metody pak můžeme jako skutečný parametr použít instanci jakékoliv třídy, která implementuje dané příslušné rozhraní. interface Zobrazitelny { void zobraz(int x, int y); void presun(int offsetx, int offsety); void zmiz(); class UkazkovaTrida void ukazkovametoda(zobrazitelny o) { o.zobraz(5, 8); o.presun(3, 14); o.zmiz(); Když vidíme zcela jednoznačné podobnosti v jednotlivých třídách, ale jejich začlenění do společného předka

5 z 9 14.1.2015 22:58 může být obtížné, protože by byl evidentně vykonstruovaný, může být nemožné, pokud naše třídy vznikly děděním z knihovních (tedy námi neovlivnitelných) tříd. Operátor instanceof Operátor instanceof zjišťuje, zda je objekt instancí dané třídy, instancí potomka nebo instancí třídy, které implementuje dané rozhraní. Vrací hodnotu typu boolean. za situace class Rodic{... interface MojeRozhrani{... class Potomek extends Rodic implements MojeRozhrani{...... Rodic obj1 = new Rodic(); Rodic obj2 = new Potomek(); bude operátor instanceof vracet tyto hodnoty: obj1 instanceof Rodic vrátí true, obj1 instanceof Potomek vrátí false, obj1 instanceof MojeRozhrani vrátí false, obj2 instanceof Rodic vrátí true, obj2 instanceof Potomek vrátí true, obj2 instanceof MojeRozhrani vrátí true. Potomek nedědí členské proměnné a metody, které: jsou deklarovány jako soukromé (private), Překrývání jsou deklarovány v jiném balíku a nejsou veřejné (public) nebo chráněné (protected), mají v potomkovi stejné jméno (a typy parametrů u metod) jako v rodičovské třídě. Tyto členské proměnné a metody jsou předefinovány hovoří se o zastínění (hiding) proměnných a překrytí (overriding) metod. Potomek překrývá metodu předka tehdy, pokud definuje metodu se stejnou hlavičkou a stejným návratovým typem. Definice metody v potomkovi se stejnou hlavičkou a různým návratovým chybem je chyba! POZOR! Při překrývání metod nelze omezit jejich přístupnost. Např. nelze public metodu překrýt metodou protected. K překrytým metodám a zastíněným proměnným se lze v případě potřeby dostat pomocí klíčového slova super: super.metoda(...), resp. super.promenna. V Javě 1.5 a výše se překrytí doporučuje explicitně vyznačit anotací @override. Kompilátor poté kontroluje, zda skutečně dochází k překrytí a ne k přetížení (v případě chybně zadaných typů parametrů) nebo dokonce k vytvoření zcela nové metody (při chybně zadaném názvu metody). Každý objekt v Javě je potomkem objektu Object je běžnou technikou překrývat metody zděděné ze třídy Object: boolean equals(object obj) String tostring() int hashcode() Překrytí metody equals Metoda boolean equals(object obj) slouží k porovnání objektů. (Operátor == porovnává pouze reference, tedy zda dvě objektové proměnné odkazují na stejné místo v paměti). Programátor si ve své třídě překrytím metody equals určí podmínky pro označení dvou objektů za stejné (rovné). Příklad dvě osoby budeme považovat za stejné, pokud budou mít stejná rodná čísla: class Osoba { private String adresa; private int rodnecislo; // Rodné číslo není příliš vhodné ukládat jako číslo, ale to nyní pro jednoduchost ignorujme. public void setadresa(string adresa) { this.adresa = adresa; public void setcislo(int rodnecislo) { this.rodnecislo = rodnecislo; @Override // tato anotace vyznačuje překrytí public boolean equals(object obj) { if (obj instanceof Osoba) { Osoba b = (Osoba) obj; return (this.rodnecislo == b.rodnecislo); return false;

6 z 9 14.1.2015 22:58 Osoba o1 = new Osoba(); Osoba o2 = new Osoba(); Osoba o3 = new Osoba(); o1.setadresa("praha"); o2.setadresa("brno"); o3.setadresa("ostrava"); o1.setcislo(9154185425); o2.setcislo(9154185425); o3.setcislo(8609065146); System.out.println(o1.equals(o2)); // vypíše TRUE System.out.println(o1.equals(o3)); // vypíše FALSE Při překrývání metody equals() musíme dodržet následující pravidla: musí být reflexivní, tj. x.equals(x) == true, musí být symetrická, tj. x.equals(y) == true právě tehdy, když y.equals(x) == true, musí být tranzitivní, tj. když x.equals(y) == true a y.equals(z) == true, tak x.equals(z) musí vrátit také true. musí být konzistentní, tj. pro nezměněné instance vrací vždy stejnou hodnotu, pro parametr null musí vracet hodnotu false, tj. x.equals(null) vrátí false. Překrytí metody hashcode public int hashcode() Pokud překryjeme metodu equals, vždy musíme překrýt i metodu hashcode. Musí platit: Pro tentýž objekt musí hashcode() vracet vždy stejný hešovací kód. Rozhodla-li equals(), že jsou si dva objekty rovny, musí hashcode() vrátit stejný hešovací kód. Nejsou-li si objekty rovny podle equals(), mohou mít stejný hešovací kód. Samostudium: Když překrýváte equals, vždy překryjte také hashcode Metoda hashcode Finální metody Pokud z jakéhokoliv důvodu chceme znemožnit překrytí nějaké metody v případných podtřídách, označíme ji jako finální. Použijeme klíčové slovo final v deklaraci hlavičky metody. public final int geti() {return i; Poznámka: Označení metody jako finální zabrání jejímu překrytí, ne však přetížení! Abstraktní metody a třídy Abstraktní metoda je v jistém smyslu opakem metody finální. Nejenže je možné ji v odvozené třídě překrýt, je to dokonce nutné (pokud tato podtřída není také abstraktní). Jinak se program nepřeloží. Takto můžeme programátora přinutit, aby při použití naší třídy jako rodičovské naprogramoval určené metody. Třída GrafickyObjekt definuje abstraktní metody vykresli() a posun(), aby odděděné třídy (již konkrétních) grafických objektů (např. Kruh) poskytovali metody k vykreslení a posunu objektu. Konkrétní definice těchto metod budou totiž v třídách (Kruh, Ctverec,...) různé, takže je není možné provést v obecné třídě GrafickyObjekt. Pro označení abstraktní metody slouží klíčové slovo abstract. Abstraktní metody se nedefinují, ale pouze deklarují v podobě hlavičky bez těla. Např. abstract void vykresli(); Třída, která obsahuje alespoň jednu abstraktní metodu, se označuje celá jako abstraktní (uvedením klíčového slova abstract v hlavičce třídy) a tudíž není možné vytvářet její instance. Taková třída je určena pouze k dědění. Podtřída takové třídy, pokud není také abstraktní, musí překrýt všechny metody, které jsou označeny jako abstraktní. Poznámka: Pokud třída implemetuje rozhraní, může obejít povinnost implementovat všechny metody rozhraní tím, že bude abstraktní: public interface Rozhrani { int max(int a, int b); int min(int a, int b); public abstract class ImplementujicicRozhrani implements Rozhrani { @Override public int min(int a, int b) { return a < b? a : b;

7 z 9 14.1.2015 22:58 Finální třídy Na rozdíl od abstraktních metod a tříd nemusí být třída, která obsahuje finální metody, třídou finální. Pokud ji však jako finální označíme (public final class XY {...), není možné tuto třídu použít jako rodičovskou (a tím pádem ani překrýt žádnou z jejích metod). Významná vlastnost OOP. Polymorfismus (mnohotvarost) Polymorfismus umožňuje, aby instance různých tříd na stejný podnět (na volání stejné metody) mohly reagovat různě. Instance více tříd pak poskytují svému okolí stejnou službu, ale každá instance na vyžádání této služby provede něco jiného. Polymorfismus dovoluje jednotným způsobem manipulovat s příbuznými objekty (mají společného předka). Základní trik polymorfismu vyplývá z toho, že potomek může nahradit předka, tzn. může být použit všude tam, kde je očekáván předek (nadtřída nebo rozhraní). Třídy Kruh a Obdelnik budou mít společného předka abstraktní třídu GrafickyObjekt (viz výše). Obě podtřídy pak musí implementovat metodu vykresli(), která však bude kruh vykreslovat samozřejmě jinak, než obdélník. Přestože nelze vytvořit instanci abstraktní třídy GrafickyObjekt, lze deklarovat proměnnou tohoto objektového typu. Do této proměnné lze pak umístit odkaz jak na instanci třídy Kruh, tak i Obdelnik, např.: GrafickyObjekt o = new Obdelnik(10, 10, 3, 4); GrafickyObjekt o = new Kruh(12, 15, 5); Abstraktní třídu GrafickyObjekt lze použít i jako návratový typ metody nebo typ parametru metody. Ve skutečnosti se pak bude opět pracovat s instancemi tříd Kruh nebo Obdelnik. Brzká vazba vs. pozdní vazba Jedná se o vazbu mezi objektem a metodou, přesněji mezi příkazem volajícím metodu a konkrétní implementací metody. V případě brzké vazby (early binding) je již při překladu programu příkaz volání metody nahrazen skokem na určitou předem danou adresu v paměti, kde je kód metody uložen. Brzkou vazbu tedy vytváří překladač (kompilátor) programu. V případě pozdní vazby (late binding) nemusí být při překladu programu jasné, která implementace metody má být spuštěna. Vazba je tedy vytvořena runtime systémem až v okamžiku provádění příkazu volání metody. Konkrétní implementace metody je vybrána z tabulky virtuálních metod (Virtual Method Table) podle skutečného typu objektu, nikoliv podle deklarovaného. V okamžiku překladu nelze určit, jestli má být volána metoda vykresli() třídy Kruh nebo třídy Obdelnik: GrafickyObjekt o;... if (a > b) o = new Kruh(5, 8, 3); else o = new Obdelnik(6, 7, 3, 2); o.vykresli(); V Javě jsou všechny metody virtuální, tzn. používá se výhradně pozdní vazba. Vnitřní třídy Platí pravidlo, že v jednom souboru se zdrojovým kódem může být nanejvýš jedna třída s přístupem public. Lze však do třídy přidávat další vnitřní třídy, které jsou jí podřízené: public class A { public class B { public class C { public static class D { public static class E { Kompilátor přeloží každou třídu do samostatného.class souboru. Vnitřní třídu pojmenuje tak, že před její jméno přidá jména nadřízených tříd oddělená znakem $, tj. vyprodukuje soubory A$B$C.class A$B.class A.class A$D.class A$D$E.class. Trochu překvapivá je syntaxe volání konstruktorů u nestatických vnitřních tříd, protože potřebují odkaz na instance nadřízené třídy: A a = new A(); A.B b = a.new B(); A.B.C c = b.new C(); A.D d = new A.D(); A.D.E e = new A.D.E(); Vnitřní třídy mají přístup k privátním proměnným nadřízených tříd: public class A { private int a = 1;

8 z 9 14.1.2015 22:58 public class B { private int b = a; public class C { private int c1 = a; private int c2 = b; Častým problémem je, jak zavolat ve vnitřní třídě metodu obklopující třídy, pokud má vnitřní třída metodu stejného jména. Pak se přistupuje pomocí kvalifikátoru JménoObklopujícíTřídy.this, tj. public class A { public String metoda() { return "A"; public class B { public String metoda() { return "B"; public class C { public String metoda() { return "C"; public void volani() { System.out.println("C=" + metoda()); System.out.println("B=" + B.this.metoda()); System.out.println("A=" + A.this.metoda()); K omezení přístupu do vnitřních tříd lze také používat modifikátory přístupu private, public a protected. Možné důvody k použití vnitřních tříd: Logické seskupení tříd, které jsou využity v jednom místě aplikace. Když je třída A využita pouze ve třídě B, je logické ji do třídy B vnořit. Mj. se tím omezí počet souborů aplikace (přehlednost). Řešení problému zapouzdření. Uvažujme dvě samostatné třídy A a B, kde B potřebuje přístup k prvkům třídy A, které by jinak byly soukromé (private). Vnořením třídy B do A mohou být prvky třídy (statické metody + proměnné) deklarovány jako soukromé a třída B k nim má přesto přístup. Navíc, třída B může být skryta před vnějším světem. Vnitřní třídy mohou vést k přehlednějšímu a lépe udržovatelnému kódu. Vnořováním jednoduchých tříd do jiných tříd lze umístit kód blíže k místu jeho použití. Anonymní třídy Mnohdy potřebujeme vytvořit instanci podtřídy nějaké třídy, případně nějakého interface, ale použít ji jen jednou, takže je zbytečné vymýšlet jméno této třídy. Proto existuje syntaxe vytvářející anonymní vnitřní třídu: new TridaNeboInterface() {... implementace anonymni tridy... Kompilátor pak vytvoří vnitřní třídu, která bude pojmenovaná jen číslem, tedy např. A$1.class. Příklad neanonymní třída: class ReagujeNaTlacitko implements java.awt.event.actionlistener { public void actionperformed(java.awt.event.actionevent e) { // zpracování stisku tlačítka; javax.swing.jbutton tlacitko = new javax.swing.jbutton("ok"); tlacitko.addactionlistener(new ReagujeNaTlacitko()); Příklad anonymní třída: javax.swing.jbutton tlacitko = new javax.swing.jbutton("ok"); tlacitko.addactionlistener(new java.awt.event.actionlistener() { public void actionperformed(java.awt.event.actionevent e) { // zpracování stisku tlačítka; ); V Javě může anonymní třída využívat lokálních proměnných pouze pokud jsou final (tzn. konstanty), tj.

9 z 9 14.1.2015 22:58 final int a = 1; ActionListener posluchac = new ActionListener() { public void actionperformed(actionevent e) { System.out.println("a = " + a); ; Lokální vnitřní třídy Vnitřní třída může být deklarována i v těle metody nebo dokonce jen bloku příkazů. Její použitelnost je pak omezena jen na tuto metodu nebo blok. Anotace kompilátoru Od verze 5 je v jazyce Java možné přidávat do programu metadata ve formě anotací. Anotace mohou být přidány k jakémukoliv programovému elementu, jako jsou třídy, balíky, metody, globální a lokální proměnné. Kromě možnosti vytváření vlastních anotací v Javě existuje pět standardních anotací používaných kompilátorem: @Deprecated touto anotací máme možnost označit element jako deprecated (zavržený), tzn. že by neměl být už dále používán. Elementem se rozumí třída, metoda, field (proměnná), konstruktor nebo interface. Při použití takového prvku v programu vypisuje překladač varovné hlášení. Takto označený eleemnt by měl být v JavaDoc dokumentaci opatřen vysvětlením, proč je zavržen a co má být použito místo něho. /** * @deprecated * Metoda je zavržena, protože... Použijte místo ní metodu... */ @Deprecated static void deprecatedmethod() { @Override tato anotace říká kompilátoru, že daný element je překrytím elementu z nadřazené třídy (rodiče). Kompilátor provádí kontrolu, zda překrytí proběhlo v pořádku (zda nejde např. o přetížení u metod). @Override public String tostring() { return "Kruh o poloměru " + r + "."; @SupressWarning pomocí této anotace můžeme kompilátoru říci, že pro daný element nemá generovat specifický warning. Můžeme tak např. potlačit vygenerování varování o tom, že používáme metodu nebo třídu, která je označena jako deprecated. @SuppressWarnings("deprecation") void usedeprecatedmethod() { objectone.deprecatedmethod(); // Jinak by zde překladač vypisoval varování. @SafeVarargs ujištění, že metoda používající proměnný počet parametrů neobsahuje potenciálně nebezpečné operace. Používá se od verze 7 jazyka Java. @FunctionalInterface anotace označující funkční rozhraní, což je rozhraní obsahující pouze jednu hlavičku metody. Používá se od verze 8 jazyka Java. T h e E n d