Programovací jazyk Java
1z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
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. Příklad: 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. Příklad: 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. Příklad: co vypíše následující program?
14.1.2015 22:58
Programovací jazyk Java
2z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
public static void main(String[] args) { 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. Příklad: public class Retez1 { public static void main(String[] args) { 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 } }
Inicializované pole řetězců public class Retez2 { public static void main(String[] args) { String[] pole = {"Dana", "Eva", "Martina"}; for (int i = 0; i < pole.length; i++) System.out.println(pole[i]); } }
Práce s řetězci String Porovnání dvou řetězců int compareTo(String str) – porovná dva řetězce 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ší. Příklad: 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
Spojení řetězců Buď operátorem + nebo metodou String concat(String str)
Náhrada všech znaků v řetězci Metoda String replace(char nahrazovany, char nahrazujici), resp. String replace(CharSequence target, CharSequence replacement). Příklad:
14.1.2015 22:58
Programovací jazyk Java
3z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
public class Nahrada { public static void main(String[] args) { 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 Získání znaku Metoda char charAt(int index) vrací znak s indexem index.
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. Příklad: 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
14.1.2015 22:58
Programovací jazyk Java
4z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
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.
Konstrukce rozhraní 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().
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. Příklad: 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 { public static void main(String[] args) { 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í. Příklad: 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
14.1.2015 22:58
Programovací jazyk Java
5z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
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. Příklad: 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 obj1 obj1 obj2 obj2 obj2
instanceof instanceof instanceof instanceof instanceof instanceof
Rodic vrátí true, Potomek vrátí false, MojeRozhrani vrátí false, Rodic vrátí true, Potomek vrátí true, MojeRozhrani vrátí true.
Překrývání Potomek nedědí členské proměnné a metody, které: jsou deklarovány jako soukromé (private), 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; } public static void main(String[] args) {
14.1.2015 22:58
Programovací jazyk Java
6z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
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. Příklad: 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. Příklad: 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; } }
14.1.2015 22:58
Programovací jazyk Java
7z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
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).
Polymorfismus (mnohotvarost) Významná vlastnost OOP. 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í). Příklad: 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. Příklad: 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;
14.1.2015 22:58
Programovací jazyk Java
8z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
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: public static void main(String[] args) { 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: public static void main(String[] args) { 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.
14.1.2015 22:58
Programovací jazyk Java
9z9
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/java_03/java_03...
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. Příklad: /** * @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). Příklad: @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. Příklad: @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.
The E nd
14.1.2015 22:58