Výjimky

Zopakujte si, co jsou výjimky, jak se chovají a jakými příkazy se s nimi zachází (try, catch, throw).

Význam výjimek

Na výjimku nahlížejte jako na součást kontraktu metod.

Tvoříme-li metodu, děláme tím kontrakt s jejími uživateli o tom, jaké budou parametry a jaký bude jejich význam; jakou vrátíme návratovou hodnotu a jaký bude její význam; jaké budou side-effects volání apod. A stejně tak je součástí tohoto kontraktu, jaké výjimky mohou vzniknout a jaký budou mít význam.

V určitých případech je možné určité tzv. runtime výjimky považovat za implicitní součást kontraktu (tj. ani nepopsanou v Javadocu). Například:

/**
* Find student by their UID.
* @param uid UID of the student. Never null.
* @return the student name or null if not found.
*/
public String findStudentNameByUid(String uid);

Tato metoda ve svém kontraktu říká, že očekává řetězec uid, který nesmí být null. Pokud by došlo k volání s parametrem null, nemělo by překvapit, že to dopadne vyhozením NullPointerException, přičemž implementátor ani nemusel explicitně kontrolovat ne-null-ovost argumentu, NullPointerException prostě vznikne někde v implementaci metody při prvním použití uid.

Poznamenejme ale, že takovýto implicitní kontrakt se v praxi týká vlastně jen uvedené NullPointerException (null tam, kde nesmí být), případně IndexOutOfBoundsException (do metody vstupuje číselný index mimo rozsah), IllegalArgumentException (rozpor s požadavky na parametry) a IllegalStateException (rozpor volání, parametrů a vnitřního stavu objektu). U posledních dvou je však běžné požadavky popsat v Javadocu; implicitní může být spíš jen typ výjimky, na niž chybné chování povede.

Druhy výjimek v Javě

Vše, co jde v Javě "vrhnout", je potomkem třídy Throwable. Nás zajímá zejména strom podtříd třídy Exception. V Javě rozlišujeme dva typy výjimek:

  1. Tzv. "checked" exceptions, což je třída Exception a všechni její potomci kromě třídy RuntimeException. Používáte-li kód, který deklaruje vrhání takové výjimky, compiler Vás bude nutit tuto výjimku ošetřit (try + catch) nebo u svého kódu (metody) deklarovat, že tuto výjimku může vrhnout (klíčové slovo throws v deklaraci - přenášíte tím zodpovědnost za ošetření výjimky na volajího). Checked exception samozřejmě můžete vrhnout i sami, pak musí být v deklaraci Vaší metody.
  2. Tzv. "unchecked" nebo též runtime exceptions, což je třída RuntimeException a její potomci. Používáte-li kód, který může takovou výjimku vrhnout, můžete a nemusíte ji ošetřit. A můžete a nemusíte ji uvést v deklaraci metody (throws). Pokud jde o svým významem zásadní výjimku pro danou metodu, je slušnost ji uvést v deklaraci throws a samozřejmě také v Javadoc.

Ošetření výjimek a výjimečných stavů vs. kontrakt metody

Řekněme, že v určitém místě kódu může vznikat výjimka (ať už checked, kde Vás na to upozorní kompilátor, nebo unchecked, kde si potřebu jejího ošetření musíte odvodit z kontraktu použité metody). 

  1. Výjimku chytíte a v jejím ošetření pokračujete "jinak". Samotné zachycení výjimky tak pro má význam if - else. Nejde o nejběžnější případ. Příklad použití: pomocí konstrukce třídy URL testuji, zda má řetězec formát URL.
  2. Výjimku nebudeme ošetřovat, prostě vyletí "výš" (v případě checked výjimek nutno přidat do deklarace). Tento případ má smysl jen a pouze tehdy, když jde o výjimku, která je součástí kontraktu Vaší metody. Jinými slovy - pokud vůbec dává ve své podobě volajícímu smysl. Například: vytvářím komponentu pro zjišťování cen akcií, která načítá data z XML. Pro čtení se používá SAX, může vzninout SAXParseException. S touto výjimkou však nemůžeme obtěžovat uživatele naší komponenty pro informace o akciích - je to naš interní věc a nikdo z venku jí nerozumí. Místo toho, je potřeba na stav reagovat dle bodu 3.
  3. Chytíme výjimku jednoho typu, vyhodíme výjimku jiného. Jde o poměrně běžnou situaci, kdy místo našeho interního problému reportujeme ven problém, jemuž volající rozumí. V uvedeném příkladu bychom tak nenechali "vyletět" SAXParseException, ale deklarovali bychom si vlastní - např. StockDataAccessException, a tu bychom vyhodili. Běžné je původní výjimku dávat jako parametr k nové výjimce pro případ diagnostiky. Proto mají výjimky jako parametr konstruktoru jinou výjimku.

Příklad kódu s vyhozením jiné výjimky:

try {

} catch (SAXParseException e) {
throw new StockDataAccessException(e)
}

Vyhazování výjimek se samozřejmě nemusí dít jen, pokud chytíme cizí výjimku. Výjimečný stav můžeme detekovat jinými prostředky jazyka a vyhozením výjimky reagovat. Např.:<-p>

if (!stockExchangeDataSource.isOnline()) {
    throw new StockDataAccessException("Not online.")
}

Co rozhodně nedělat

  • Ne: "požírání" nesouvisejících výjimek
  • Ne: zahození
  • Ne: "ošetření" pomocí printStackTrace() a následné porušení kontraktu (k čemuž může svádět to, "co jste se učili").

Zuletzt geändert: Montag, 17. Juni 2013, 00:50