الواجهات التعريفية والمختومة
الواجهات التعريفية والمختومة
ليست كل الواجهات موجودة لوصف السلوك من خلال عقود الأساليب. تحلّ فئتان متخصّصتان — الواجهات التعريفية (Marker Interfaces) والواجهات المختومة (Sealed Interfaces) — مشكلتين مختلفتين جوهريًا: الأولى تلصق بيانات وصفية بالفئة، والثانية تقيّد الفئات المسموح لها بتنفيذ الواجهة. يظهر كلا النمطين في كود Java الحقيقي، لذا فهم متى تستخدم كلًا منهما وسبب ذلك أمر ضروري.
الواجهات التعريفية
الواجهة التعريفية لا تحتوي على أيّ أساليب. غرضها الوحيد هو وسم الفئة حتى تتمكن الشيفرة الأخرى — عادةً إطار العمل أو JVM نفسه — من اكتشاف هذا الوسم في وقت التشغيل باستخدام instanceof أو الانعكاس (Reflection).
عندما تستقبل آلية التسلسل في JVM كائنًا ما، تتحقق من obj instanceof Serializable قبل المتابعة. إذا لم تكن الفئة موسومة، يُطلق NotSerializableException. لا توجد توقيعات أساليب؛ كل منطق التسلسل الفعلي يقع داخل JVM وفي ObjectOutputStream.
من الواجهات التعريفية الأخرى المعروفة في JDK: Cloneable التي تُفعّل آلية النسخ المحمية Object.clone()، وRandomAccess التي تُشير إلى أن القائمة List تدعم الوصول المُفهرَس O(1) مما يتيح للخوارزميات اختيار مسار أسرع.
كتابة واجهة تعريفية خاصة بك
يمكنك تعريف واجهة تعريفية لإرفاق معنى دلالي بالفئات في تطبيقك الخاص:
@Idempotent حمل نفس البيانات الوصفية بأقل تعقيد، بل يمكنها أيضًا حمل سمات (مثل @Idempotent(retries = 3)). في الكود الجديد، يُفضَّل استخدام التوصيفات بدلًا من الواجهات التعريفية إلا إذا كنت بحاجة فعلية لفحص نظام الأنواع عبر instanceof أو قيد جنيري مثل <T extends Idempotent>.
الواجهات المختومة (Java 17)
تُدرج الواجهة المختومة صراحةً كل فئة (أو واجهة) مسموح لها بتنفيذها. أيّ فئة غير مدرجة في القائمة تُسبّب خطأ في وقت الترجمة. هذا يمنح مؤلف المكتبة تحكمًا دقيقًا في التسلسل الهرمي للأنواع.
يجب الإعلان عن كل فئة مسموح لها بإحدى طرق ثلاث:
final— تُغلق التسلسل الهرمي نهائيًا؛ لا مزيد من الاشتقاق.sealed— الفئة المسموح لها بدورها تُقيّد أنواعها الفرعية.non-sealed— تُعيد فتح التسلسل الهرمي؛ يمكن لأي شخص توسيع هذه الفئة أو تنفيذها.
لماذا الختم مهم: مطابقة الأنماط الشاملة
العائد الحقيقي من الواجهات المختومة يكمن في تعابير switch المُقدَّمة في Java 17 فأحدث. لأن المُترجِم يعرف كل منفّذ محتمل، يمكنه التحقق من أن تعبير switch الخاص بك شامل — لا حاجة لفرع default:
إذا أضفت لاحقًا Pentagon إلى قائمة permits، يصبح كل switch على Shape يفتقر لحالة Pentagon خطأ في وقت الترجمة — المُترجِم يرشدك إلى كل موقع استدعاء يحتاج تحديثًا.
final، لذا تفي بشرط الختم بشكل مثالي:
الواجهات المختومة مقابل التعدادات (Enums)
التعداد enum مثالي عندما لا يحمل كل متغيّر بيانات إضافية وتريد مجموعة ثابتة من الثوابت. الواجهة المختومة هي الخيار الصحيح عندما تحمل متغيّرات مختلفة أشكالًا مختلفة من البيانات — مثلًا Triangle له قياسان بينما Circle له قياس واحد. تكمّل ميزتا بعضهما ويمكن دمجهما: يمكن للتعداد enum تنفيذ واجهة مختومة ليصبح أحد متغيّراتها المسموح بها.
الخلاصة
تُلصق الواجهات التعريفية وسمًا بالفئة يمكن للشيفرة في وقت التشغيل اكتشافه عبر instanceof؛ يستخدم JDK هذا النمط في Serializable وCloneable وRandomAccess. في الكود الجديد، التوصيفات غالبًا بديل أنظف إلا إذا احتجت قيدًا في نظام الأنواع. أما الواجهات المختومة المُضافة في Java 17، فتُقيّد من يمكنه تنفيذ الواجهة، محوّلةً المُترجِم إلى حارس للتسلسل الهرمي لأنواعك ومُتيحةً مطابقة الأنماط الشاملة في تعابير switch — أداة قوية لنمذجة المجموعات المغلقة من الأنواع المترابطة.