JavaSvet - otvorena java zajednica

 
glavna stranica arr2javasvet  english version arr2java.net

To check or not to check?

Nemanja Kostić
18 Maj 2004

U Javi postoje dve vrste izuzetaka, checked i unchecked. Checked izuzeci proširuju klasu java.lang.Exception i kompajler insistira da oni budu uhvaćeni ili eksplicitno bačeni. Unchecked ili Runtime izuzeci proširuju klasu java.lang.RuntimeException i oni ne moraju biti uhvaćeni. Java je jedini viši OO jezik koji podržava Checked izuzetke. Svi izuzeci u C++ ili C# jeziku su ekvivalent Runtime izuzecima u Javi.

Preporuka prema Java tutorijalima je da se uvek koriste Checked izuzeci jer se na taj način može u potpunosti kontrolisati rad sistema. Korišćenje Runtime izuzetaka se smatra programerskom lenjošću da hvata silne izuzetke koje kompajler baca.

Ovakva teza stoji ukoliko su u pitanju manji projekti sa manje koda. Međutim, da li je ovo sasvim ispravna logika kada se radi na velikim projektima sa par hiljada klasa?

Problemi koji se sreću prilikom korišćenja Checked izuzetaka:

  1. Previše koda - programeri se često izfrustriraju kada trebaju da pišu try i catch blokove na svakom koraku. Ukoliko njihova klasa ne može da obradi uhvaćeni izuzetak, ona mora da ga propagira dalje na viši nivo. Međutim, često se dešava da programeri ne znaju ili zaborave da pravilno ugnežđuju izuzetke tako da se stack trace potpuno gubi. A bez njega, ne može da se zna šta je tačno prouzrokovalo isti (čuvena "Root cause:" poruka).

  2. Nečitak kod - hvatanje izuzetaka koji ne mogu da se obrade zahteva njihovo prebacivanje u drugu vrstu izuzetka koji se zatim baca dalje (exception wrapping).

  3. Beskrajno umotavanje izuzetaka - ukoliko je bačeni izuzetak nepopravljiv, njegovim umotavanjem u neki drugi izuzetak se ništa ne postiže. Recimo da se javi greška da je fajl sistem napunjen ili da je remote konekcija prekinuta ili nešto treće. Umesto da se na tom mestu odmah loguje kompletan stack trace kao što se dešava sa Runtime izuzecima, sa Checked izuzecima se isti taj stack trace prosleđuje dalje da bi na nekom visokom nivou morali da napišemo nekoliko linija koda koji ručno loguju taj trace.

  4. Održavanje koda - ukoliko se neki metod poziva sa puno strana, dodavanje novog izuzetka u taj metod, zahteva promenu koda u svim ostalim metodima.

Ovi problemi nikako ne treba da sugerišu da se Checked izuzeci potpuno izbace. Naprotiv, treba da pokažu da je potrebno shvatiti kada koristiti koje izuzetke. Na primer, ukoliko klijent kupuje neku robu preko debitne kartice a nema dovoljno novca na računu, bacanje recimo PriceExceedsLimitException ima punog smisla jer je to nešto na šta sam korisnik može da utiče. Kupiće ono što može sebi da priušti. Sa druge strane, bacanje greške ConnectionToDatabaseFailedException jer se neki biznis metod nije mogao konektovati na bazu iz nekog razloga, za krajnjeg korisnika nema apsolutno nikakvog značaja. On tu ništa ne može da promeni. RuntimeException u ovakvom slučaju ima smisla koristiti.

Kada se baci RuntimeException, to obično dovodi do suspenzije Thread-a, ali to ne treba mnogo da nas brine jer kontrola Thread-ova se ionako vrši u J2EE kontejneru. Kontejner će suspendovati taj Thread (uništiće EJB instancu) i poruku logovati. Ukoliko je greška fatalna, kao što je recimo nemogućnost konekcije na bazu, EJB instanca i treba da bude ugašena jer nam ničemu više ne služi. Ne može da obavi traženi posao.

Hint: Da se ne bi izgubio stack trace prilikom umotavanja jednog izuzetka u drugi, od Jave 1.4 je uveden novi konstruktor u Exception klasu koji se koristi kao:

catch (RootCauseException ex) {
   
throw new MyJavaException("Detailed message", ex);
}

Ukoliko se koristi kod koji je pisan u starijoj Java verziji, i dalje se može izbeći gubitak stack trace-a, bacanjem izuzetka na sledeći način:

catch (RootCauseException ex) {
   
MyException mex = new MyException("Detailed message");
    mex.initCause
(ex);
   
throw mex;
}