instanceof المحسّن وأنماط السجلّات
instanceof المحسّن وأنماط السجلّات
قبل Java 16، كان التحقّق من نوع كائن ثم استخدامه بذلك النوع يستلزم خطوتين منفصلتين: فحص instanceof ثم تحويل صريح (cast). قدّمت Java 16 مطابقة الأنماط لـ instanceof، وامتدّت Java 21 لتشمل أنماط السجلّات — ممّا يتيح تفكيك مكوّنات السجلّ مباشرةً داخل النمط. معًا تُزيل هذه الميزات فئةً من الشيفرة الزائدة عن الحاجة موجودة في Java منذ إصدارها الأوّل.
الطريقة القديمة: الفحص ثم التحويل
كتب كلّ مطوّر Java شيفرةً كهذه في مرحلة ما:
التحويل في السطر الثاني مجرّد إجراء شكلي. يعلم المُجمِّع بالفعل أنّ النوع صحيح لأنّ فحص instanceof نجح. الكاست موجود فقط لأنّ اللغة كانت تستلزمه.
مطابقة الأنماط لـ instanceof
مع مطابقة الأنماط تجمع الفحص والربط في تعبير واحد:
متغيّر النمط c يكون في النطاق فقط حيث تكون المطابقة مضمونة. يُطبّق المُجمِّع هذا القيد: لا يمكنك استخدام c عن طريق الخطأ في فرع لم تنجح فيه المطابقة.
if، وليس في فرع else وبالتأكيد ليس خارج التعبير. يرفض المُجمِّع أيّ محاولة لاستخدامه حيث لا تكون المطابقة مضمونة.
استخدام متغيّر النمط في الشروط
يمكن أن تظهر متغيّرات النمط في نفس التعبير المنطقي، ممّا يتيح حراسات مدمجة:
لأنّ Java تقصير-تحقّق (short-circuit) مع &&، فإنّ الجانب الأيمن من المُشغّل يُصل إليه فقط عند نجاح المطابقة، ممّا يضمن أنّ s هي String هناك. المنطق ذاته يعني أنّ || لن تكون آمنة — ويرفضها المُجمِّع.
مطابقة الأنماط في تعبيرات Switch (Java 21)
عمّمت Java 21 الأنماط على switch. يمكنك المطابقة على النوع وربط متغيّر وإضافة حارسة when في ذراع واحدة:
لأنّ Shape مغلقة (sealed)، يعرف المُجمِّع أنّ الأنواع الفرعية الثلاثة المسموح بها تُغطّي كل الحالات ولا يتطلّب ذراع default. إضافة نوع فرعي رابع لاحقًا ستُحوّل الذراع المفقودة إلى خطأ تصريفي — لا إخفاق صامت وقت التشغيل.
حارسات when
تُكمّل جملة when ذراع النمط بتعبير منطقي اختياري:
تُقيَّم الذراعات من الأعلى إلى الأسفل. تفوز أوّل ذراع يطابق نمط نوعها وتكون حارستها when صحيحة. هذا يجعل الترتيب ذا معنى — على عكس switch التقليدي على الأنواع البدائية، لا يوجد خطر التسقّط (fall-through).
أنماط السجلّات: التفكيك في مكانه
قدّمت Java 21 أنماط السجلّات التي تتيح مطابقة نوع السجلّ وربط مكوّناته في آنٍ واحد. بدلًا من استدعاء الدوالّ الصلة بعد المطابقة، تُسمّي المكوّنات مباشرةً في النمط:
نمط السجلّ Line(Point start, Point end) يختبر في آنٍ واحد أنّ obj هو Line ويفكّكه إلى مكوّنيه في عملية واحدة.
أنماط السجلّات المتداخلة
أنماط السجلّات قابلة للتركيب. يمكنك تداخلها للوصول عميقًا في بنية البيانات دون متغيّرات وسيطة:
النمط الداخلي Point(int x, int y) يفكّك المكوّن start. النمط الخارجي يفكّك Line. كلا الربطين في النطاق داخل الجسم.
null أبدًا. إذا كان obj هو null، يفشل النمط بالكامل فورًا — دون رمي NullPointerException. هذا متسق مع كل مطابقة أنماط في Java.
أنماط السجلّات في Switch
تبرز أنماط السجلّات أكثر في switch حيث تحتاج إلى تفكيك أشكال متعددة:
هذا مُقيَّم تعبير كامل متعاود في سبعة أسطر. تستخرج أنماط السجلّات l وr من عقدتَي Add وMul على نفس السطر الذي يحدّد نوع العقدة — لا متغيّرات وسيطة، لا دوالّ وصول.
المقايضات ومتى تستخدم هذه الميزات
- استخدم مطابقة الأنماط لـ
instanceofكلّما كنت ستكتب فحصًا يليه تحويل. إنّها أفضل بالمطلق: ضوضاء أقل، لا خطرClassCastExceptionخفي من اسم متغيّر خاطئ. - فضّل التسلسلات الهرمية المغلقة + أنماط switch على سلاسل
if/else instanceofالطويلة. فحص الاستيعاب الشامل يجعل الشيفرة أكثر متانة وأسهل صيانةً. - استخدم أنماط السجلّات عندما تكون بنية البيانات هي محور الاهتمام. تجعل الشيفرة الخوارزمية التي تجتاز البيانات المتعاودة (أشجار التعبير، مُحلِّلات AST، نماذج JSON) أوضح بكثير.
- لا تُبالغ في التداخل. أنماط السجلّات المتداخلة عميقًا (
A(B(C(D d)))) يصعب قراءتها. إذا كانت البنية أعمق من مستويين، فاستخراج متغيّر وسيط أوضح في الغالب.
الخلاصة
تستبدل مطابقة الأنماط لـ instanceof (Java 16) نمط الفحص ثم التحويل بتعبير ربط واحد. تمتدّ أنماط switch (Java 21) لتشمل الإرسال متعدّد الذراعات مع حارسات when وفحوصات الاستيعاب الشامل للأنواع المغلقة. تُضيف أنماط السجلّات (Java 21) التفكيك — ربط مكوّنات السجلّ مباشرةً في النمط. هذه الميزات الثلاث مجتمعةً تجعل الشيفرة المدفوعة بالأنواع في Java موجزةً وآمنةً ومُتحقَّقًا منها بالمُجمِّع، مُقرِّبةً اللغة من قدرات مطابقة الأنماط الموجودة في Haskell وScala وKotlin.