Bu məqalədə sertifikat imtahanına hazırlaşmağın sizə verəcəyi bir faydaya toxunacam. Dünən maraqlı bir problemlə rastlaşdım və başa düşdüm ki, sertifikat imtahanına hazırlaşmasaydım problemin həll yolunu axtarmaq bir yana qalsın, heç problemin səbəbini başa düşməyəcəkdim. Çünki fundamental java kitabları oxuyarkən çox xırda detallara elə də diqqət yetirmirik, amma sertifikat imtahanı suallarında həmin mövzularla o qədər rastlaşırıq ki, artıq beynimizin bir küncündə özünə yer edir.
Anlaşıqlı olsun deyə sadə bir kod nümunəsi yazacam və problemin nə olduğunu həmin kod üzərindən qeyd edəcəm:
package az.mm.runnable; import java.io.IOException; public class Controller { public static void main(String[] args) { Service service = new Service(); service.sendMessage(); } } class Service { void sendMessage() { Runnable runnable = new Runnable() { @Override public void run() { try { // do something throw new IOException(); } catch (IOException ex) { } } }; Thread thread = new Thread(runnable); thread.start(); } }
Tutaq ki, bir Controller
classı var, web servisə gələn sorğuları qəbul edir və Service
classındakı sendMessage()
metodunu işlədir. Çox detallara getməyəcəm, koddan hər şey aydın görünəcək. Deməli burada çatışmazlıq nədir: əgər run()
metodunda xəta baş versə, catch
blokunda xəta yaxalanacaq və Controller
classının bundan xəbəri olmayacaq. Əslində bizə lazımdır ki, run()
metodunda xəta baş verərsə, bu xətanı Controller
classına ötürək və Controller
classı da öz növbəsində bu xətanı sorğunu göndərən clint`ə bildirsin. Client də mesajının göndərilmədiyi barədə məlumatlı olsun. Bunun üçün biz catch
blokunda xətanı qeydə alıb logladıqdan sonra yenidən onu Controller
classına throw
edə bilərik, yəni bu formada:
try { // do something throw new IOException(); } catch (IOException ex) { System.out.println(ex); throw ex; // line a }
Amma “line a” sətri compile xətası verir. Bax işin bütün məğzi bu xətanın səbəbini tapmaqdır və bu mövzuda toxunmaq istədiyim əsas məsələ də elə budur. Artıq burada sertifikat imtahanına hazırlaşarkən öyrəndiklərimiz köməyimizə çatır 🙂
Əvvəlcə sadə yanaşma ilə fikirləşə bilərik ki, əgər catch
blokunda yenidən “checked exception” fırladılarsa, o yenidən ya “handle” edilməlidir, ya da “declare”. Amma bizə “declare” olunması lazımdır, bunun üçün public void run() throws IOException
yazmalıyıq. Amma bu da bizə kömək etmir, yenə compile xətası alırıq, bir az daha dərinə enməliyik.
run()
metodu Runnable
interfeysinə aid olan abstract
metoddur və biz onu override edirik. Compile xətasının səbəbi də metodlar üçün override olunma qaydalarının pozulması ilə əlaqədardır. Əvvəlcə baxaq ki, düzgün override olunmuş metod hansı qaydalara riayət etməlidir:
- Child classdakı metod parent classdakı metod ilə eyni quruluşa (the same signature) malik olmalıdır. Yəni, metod adları və parametr siyahısı mütləq eyni olmalıdır.
- Child classdakı metodun access modifier`i parent classdakı metodla ən azı eyni olmalıdır, ya da ki, daha yüksək.
- Parent classdakı metodun fırlatdığı (
throws
) checked exceptionun tipi həmişə child classdakı metoddan daha geniş olmalıdır. Child classdakı metod maksimum halda eyni exception tipini fırlada, yaxud da kiçik tip, və yaxud da heç exception fırlatmaya da bilər. - Hər iki metodun geriyə döndürdüyü dəyər eyni tipdə olmalıdır və ya override edilən/parent classdakı metodun subclassı (covariant return types).
- *private metodlar override oluna bilməz.
Deməli, bizdə 3-cü qaydanın tələbləri pozulub, çünki Runnable
interfeysindəki run()
metodunun strukturu belədir:
-
public abstract void run();
Göründüyü kimi bu metod heç bir Exception
fırlatmır, ona görə də biz IOException
`u throws
edə bilmərik, başqa həll variantları fikirləşməliyik.
İnternetdə baxdım fərqli həllər təklif edilmişdi, amma özümün ağlına maraqlı bir həll variantı gəldi, onun üzərində dayandım, fikirləşirəm ki, işimə yarıya bilər. Deməli, metod override qaydaları ilə bağlı 3-cü şərtə diqqətlə baxsaq görərik ki, söhbət ancaq “checked” exceptionlardan gedir, bu məhdudiyyət “unchecked” exceptionlara tətbiq edilmir. Aha deməli mənə xətanın baş verib verməməsini bilmək lazımdır və əgər baş veribsə, onu mətnini client`ə göstərməliyəm. Ona görə də mən catch
blokunda “unchecked exception” fırlada və həmin exceptionun konstruktoru vasitəsilə original exceptionun mətnini göndərə bilərəm:
package az.mm.runnable; import java.io.IOException; public class Controller { public static void main(String[] args) { Service service = new Service(); service.sendMessage(); } } class Service { void sendMessage() { Runnable runnable = new Runnable() { @Override public void run() { try { throw new IOException(); } catch (IOException ex) { throw new RuntimeException(ex); } } }; Thread thread = new Thread(runnable); thread.start(); } }
Əgər thread`lərin işləmə məntiqi ilə bir uyğunsuzluq yaranmasa, ümid edirəm bu həll variantı işimə yarıyacaq. Əgər həll variantında əksikliklər olarsa və dəyişməli olsam, məqaləni yeniləyib burada qeyd edəcəm İnşallah.