throw و throws
throw و throws
في الدروس السابقة تعلّمت كيف تلتقط الاستثناءات التي يُطلقها JVM أو المكتبة القياسية. لكن أحيانًا يحتاج كودك أنت إلى الإشارة بأن شيئًا ما قد سار بشكل خاطئ. هذا ما تُقدّمه الكلمتان المفتاحيتان throw وthrows. تبدوان متشابهتين لكنّهما تؤدّيان أدوارًا مختلفة تمامًا — وفهم كلتيهما ضروري لكتابة Java واضحة وأمينة.
throw — إطلاق استثناء بنفسك
تتيح لك الكلمة المفتاحية throw إنشاء كائن استثناء وإطلاقه في أي نقطة من كودك. بمجرّد أن تصطدم Java بعبارة throw، يتوقّف تنفيذ الدالة الحالية ويبدأ الاستثناء بالسفر نحو الأعلى في مكدّس الاستدعاء بحثًا عن كتلة catch مطابقة.
لاحظ عدّة أمور:
throwتأخذ كائنًا — دائمًا تكتبthrow new SomeException("message").- تختار أكثر فئة استثناء تحديدًا تتناسب مع المشكلة.
IllegalArgumentExceptionمناسبة لمُدخلات خاطئة؛ أماIllegalStateExceptionفمناسبة حين يكون الكائن نفسه في حالة خاطئة. - يجب أن تساعد سلسلة الرسالة المستدعيَ على تشخيص المشكلة — أضف القيمة الخاطئة فيها كلّما أمكن.
-1 أو null للإشارة إلى الفشل تُلزم كل مستدعٍ بتذكّر التحقّق — وغالبًا ما ينسون. الاستثناء لا يمكن تجاهله بصمت: إذا لم يلتقطه أحد، ينهار البرنامج مع تتبّع مكدّس واضح.
throws — الإعلان عن الاستثناءات المتحقّق منها
تنتمي الكلمة المفتاحية throws إلى توقيع الدالة، ليس داخل جسمها. إنّها عقد: تُخبر المُترجم وكل مستدعٍ "هذه الدالة قد تُطلق الاستثناء المُتحقَّق منه المُدرج، وعليك التعامل معه."
بما أن IOException استثناء متحقَّق منه، يُجبر المُترجم كل مستدعٍ لـreadFile على إمّا:
- إحاطة الاستدعاء بكتلة
try-catch، أو - إضافة نفس الإعلان
throws IOExceptionإلى دالتهم الخاصة، ما يُحوّل المسؤولية نحو الأعلى.
throws. يمكن إطلاق RuntimeException وفروعها (مثل IllegalArgumentException وNullPointerException) في أي مكان دون إعلانها. فقط الاستثناءات المتحقَّق منها (تلك التي تمتد من Exception لكن ليس من RuntimeException) يجب إعلانها.
كيف ينسحب المكدّس — الانتشار
حين يُطلق استثناء ولا يُعثر على catch مطابق في الدالة الحالية، تنتقل Java إلى مُستدعي تلك الدالة وتبحث مجددًا. يستمر هذا حتى إيجاد معالج أو وصول الاستثناء لقمّة المكدّس (ممّا يُوقف الخيط).
المُخرج:
تُطلق level3 الاستثناء، ولا تلتقطه level2 ولا level1، فيسافر حتى main حيث يُعالَج أخيرًا. ينسحب مكدّس الاستدعاء تلقائيًا — لا حاجة لأي كود إضافي في level1 أو level2.
الجمع بين throw و throws
الدالة التي تُعلن استثناءً متحقَّقًا منه (بـthrows) وتُطلقه يدويًا (بـthrow) أمر شائع جدًا:
Exception أو Throwable مباشرةً. كتابة throw new Exception("something broke") تُجبر المستدعين على التقاط أوسع نوع ممكن، ممّا يُخفي ما حدث فعليًا. دائمًا افضّل فئة استثناء محدّدة وذات معنى — استخدم الفئات القياسية من JDK أو أنشئ فئاتك الخاصة (ستتعلّمها في الدرس 7).
مثال عملي كامل
الخلاصة
throwتُكتب داخل جسم الدالة — تُطلق كائن استثناء الآن فورًا.throwsتُكتب على توقيع الدالة — تُعلن أن الدالة قد تُطلق استثناءً متحقَّقًا منه وتُحمّل مسؤولية المعالجة للمستدعي.- الاستثناءات غير المتحقَّق منها (فروع
RuntimeException) يمكن إطلاقها بحريّة دون إعلان. - حين لا يُلتقط الاستثناء محليًا، ينتشر نحو الأعلى في مكدّس الاستدعاء تلقائيًا حتى يجد من يلتقطه أو ينهار البرنامج.