Vše co jste chtěli vědět o složených závorkách, ale báli jste se zeptat

Stejně jako každý ninja musí prokázat, že dokáže zabít i roličkou toaletního papíru, i já předvedu stupeň své nezměrné zvrhlosti tím, jak dokážu používat složené závorky. Složené závorky asi používáte všichni, ale všichni před ně určitě amatérsky píšete něco jako jméno třídy, metody, if, for nebo něco podobného. To já už jsem dosáhl vyšší úrovně zasvěcení a nepíšu před ně vůbec nic. Předem vás chci upozornit ať to doma radši nezkoušíte, předvedená technika vyžaduje léta tréninku, dřiny a odříkání.

První možností, kde se se závorkou bez ničeho můžete setkat, je inicializační blok. Vypadá to následovně.

public class UglyClass {
	{
		System.out.println("Ugly code");
	}
	{
		System.out.println(" rocks :-/");
	}
}

Krása, že? Ano, opravdu tento kód je v Javě přípustný. Tyto bloky jsou zavolány pokaždé, když je třída instancována a to v pořadí, v jakém jsou uvedeny v kódu. Výhodou má být, že je tento kód sdílený mezi všemi konstruktory. Jenom tiše doufám, že to nikdo nepoužívá. Byla by to opravdu krásná past, schovat podobný blok někam na konec třídy a tam něco ošklivého provést. Dlouho jsem přemýšlel, jestli by existovalo nějaké rozumnější využití. Nebyl by to můj ohavně zvrhlý mozek, kdyby něco nevymyslel. Tady to máte:

		List<Address> addresses = Arrays.asList(
			new Address()
			{
				{
					setStreet("Ugly 13");
					setCity("Brno");
					setZip("000000");
				}
			},
			new Address()
			{
				{
					setStreet("Ugly 15");
					setCity("Brno");
					setZip("000000");
				}
			}
		);

Představte si, že chci nainicalizovat seznam adres (třeba v unit testu) a nemám vhodný konstruktor. Tak si vytvořím anonymního potomka a v jeho inicializačním bloku si nastavím co potřebuji. Cool.

Samozřejmě normální, konzervativní varianta je takováto:

		List<Address> addresses = new ArrayList<Address>(2);
		Address address1 = new Address();
		address1.setStreet("Ugly 13");
		address1.setCity("Brno");
		address1.setZip("000000");
		addresses.add(address1);

		Address address2 = new Address();
		address2.setStreet("Ugly 15");
		address2.setCity("Brno");
		address2.setZip("000000");
		addresses.add(address1);

Nuda co? Navíc má tato varianta jeden velký problém. Často v ní udělám chybu. Jako třeba v uvedeném příkladě. Kdo si té chyby všiml rovnou, má bod, já bych ji tam hodně dlouho hledal.

Abych podobné chybě zabránil, mohu použít druhou možnost, jak napsat osamocené složené závorky. Prostě je fouknu doprostřed metody, čímž vytvořím normální blok kódu.

		List<Address> addresses = new ArrayList<Address>(2);
		{
			Address address1 = new Address();
			address1.setStreet("Ugly 13");
			address1.setCity("Brno");
			address1.setZip("000000");
			addresses.add(address1);
		}
		{
			Address address2 = new Address();
			address2.setStreet("Ugly 15");
			address2.setCity("Brno");
			address2.setZip("000000");
			addresses.add(address2);
		}

Dosáhnu tím toho, že omezím platnost lokálních proměnných. Překladač mě pak nenechá do seznamu vložit stejnou adresu dvakrát. To už je použití, které by se možná i dalo obhájit.

A teď si představte, že člověku jako já, dáte do ruky closures (uzávěry). To by pak museli čtenáře mého kódu rovnou uzavřít do blázince. Aha, už alespoň vím, proč se closures jmenují, tak jak se jmenují.

Pokud máte opravdu silné nervy, podívejte se na tento článek, tam uvidíte, že já jsem v podstatě neškodný.

14 thoughts on “Vše co jste chtěli vědět o složených závorkách, ale báli jste se zeptat

  1. Ladislav Thon

    Nějak nevidím důvod, proč by používání podobně zvrhlých konstrukcí spolu s lexikálními uzávěry mělo způsobovat větší bolesti hlavy, než kdyby se používaly spolu s anonymními vnitřními třídami, spíš naopak.

    To vytvoření samostatného bloku uvnitř metody je celkem užitečné, inicializátor instance mi přijde ve své odpornosti skoro krásný 🙂

    v6ak: chyba je na posledním řádku… 🙂

  2. Rasto

    Skutočne pekný príklad používania blokov.
    Dá sa tým vyriešiť aj problém dlhých metód, na jednej strane “Príručka mladých svišťov – Metriky kódu” hovorí, že metóda ( alebo skôr blok kódu ) by nemala byť dlhšia ako jednu stranu aby sa dala porozumieť, na druhej strane je blbosť ju rozsekať ju na viac metód, keďže každá z nich je volaná len z jednoho miesta.
    Horeuvedený príklad zlepší čitateľnosť a zachová rozumný design.

    btw: tu chybu som uz urobil niekolkokrat.

  3. ufak

    Pokud kzda inicializace bude v bloku, mohu pouzivat neustale stejne jmeno a nemusim pri kopirovani blbnout s prejmenovanim. Stejne bych mel inicializaci na jeden radek, protoze bych udelal metodu…

  4. Ales

    Tak to ja pouzivam obcas tyto inicializace i jako staticke ( static { … } ). Napriklad pri nutnosti inicializovat statickou promenou tridy typu HashMap ve tride s pouze privatnim konstruktorem a statickymi metodami. Ale aspon tyhle inicializace pisu na zacatek trid a poradne je oznacim.

    btw: klasicka copy-paste chyba, ktera se blbe hleda.

  5. benzin

    P.S.: tehhle metod se pouziva (obcas) pri inciializaci Enum promenych, kdyz do instance chcete narbat jeste nejake informace.

    Asi to jde udelat i jinak, ale proste se to pouziva.

  6. antaran

    🙂 super clanok, nic nespestri pracovny den tak, ako inicializacny blok v anonymnej triede 🙂

    PS: hadanka 🙂

    public class $pokus {

    public static void main (String…params){

    Integer i1=100, i2=100, i3=5000, i4=5000;

    System.out.println(“i1==i2 : “+(i1==i2));
    System.out.println(“i3==i4 : “+(i3==i4));

    }
    }

  7. v6ak

    Ladislav Thon: Nevím proč, ale pořád ji nevidím 🙁 setZip? Nebo závorka? Netuším. Já Javu zatím znám víc teoreticky než prakticky.

  8. v6ak

    Aha, ta chyba se týkala konzervativní varianty. Tak to je pak o něčem jiném! Tam už je ta chyba na posledním řádku vidět. 🙂

  9. Lukáš Křečan Post author

    To v6ak: Konstruktor s parametry neni problem, inicializacni blok je kompilatorem zkopirovan no kazdeho konstruktoru. Ale znovu upozornuji, ze bych to pouzival opatrne (=vubec). Programatori na to nejsou zvykli.

    To antaran: Kdyz clovek porovnava instance objektu pomoci == tak se nesmi divit

  10. antaran

    To Lukáš Křečan:

    re: == …je to len hadanka na pobavenie…chcel som vediet, ci niekto len tak zbrucha odpovie spravne 🙂

    re: inicializacne bloky, najdolezitejsie je si uvedomit ako to funguje pri dedicnosti v kombinacii s konstruktormi… 🙂 … ak toto vies, tak ta nemoze nic prekvapit 🙂

    import static java.lang.System.*;

    class A {
    A(){out.println(“CON:A”);}
    {out.println(“IB:A”);}
    static {out.println(“SIB:A”);}
    }

    class B extends A {
    B(){out.println(“CON:B”);}
    {out.println(“IB:B”);}
    static {out.println(“SIB:B”);}
    }

    public class $pokus2 {

    public static void main (String…params){
    A o = new B();
    }
    }

  11. Lukáš Křečan Post author

    Právě proto by se to mělo používat s rozmyslem (=vůbec). Takovýto kód je možná pěkný v otázce na SCJP, ale v produkčním kódu rozhodně ne.

  12. v6ak

    to Lukáš Křečan: Jasně, ale když vytvořím potomka, tak přece nezdědí konstruktor, ne? Maximálně dostane implicitní konstruktor bez parametrů, který zavolá konstruktor předka. Ale když chci parametry, tak to nestačí.

Comments are closed.