الكلمة المفتاحية final للأصناف والتوابع
الكلمة المفتاحية final للأصناف والتوابع
حتى الآن في هذه السلسلة تعلّمت كيف تُوسّع الأصناف وتُعيد تعريف التوابع لبناء تسلسلات هرمية مرنة. لكن أحيانًا تكون المرونة هي آخر ما تحتاجه. أحيانًا تريد أن تقول: "لا يجوز توسيع هذا الصنف أبدًا" أو "لا يجوز استبدال هذا التابع أبدًا." تمنحك Java كلمة مفتاحية واحدة لكلا الغرضين: final.
معنى final على التوابع
حين تُضيف final إلى تابع، لا يستطيع أي صنف فرعي إعادة تعريفه. التنفيذ الذي كتبته ثابت على طول سلسلة الوراثة بأكملها.
منطق السحب يُطبّق قاعدة عمل: لا يمكن سحب مبلغ سالب أو أكثر من الرصيد. جعله final يضمن ألا يتحايل أي صنف فرعي على تلك القاعدة.
final على تابع حين يعتمد السلوك الصحيح على تنفيذه تحديدًا — وأي إعادة تعريف من صنف فرعي ستُدخل أخطاءً أو ثغرات أمنية.
معنى final على الأصناف
حين تُضيف final إلى صنف بأكمله، لا يمكن توسيعه على الإطلاق. لا أحد يستطيع كتابة class Foo extends YourFinalClass.
لأن الصنف final وجميع حقوله private final، يُضمن أن كائن ImmutablePoint ثابت تمامًا — لا يمكن تغيير إحداثياته بعد الإنشاء، ولا يمكن لأي صنف فرعي إضافة حالة قابلة للتغيير.
أصناف final في Java — String
String هو المثال الأشهر على صنف final في مكتبة Java القياسية. لا يمكن توسيعه:
لماذا جعل مصمّمو Java صنف String نهائيًا؟ تتضافر عدة أسباب:
- الأمان. تتلقّى أجزاء كثيرة من JVM والمكتبة القياسية قيمة
Stringوتثق بها (مسارات الملفات، أسماء الأصناف، كلمات المرور). لو كانStringقابلًا للتوسيع، يمكن لصنف فرعي خبيث إعادة تعريفequalsأوtoStringللكذب بشأن قيمته بعد فحص أمني. - الثبات (Immutability). كائنات
Stringثابتة — لا يتغيّر مصفوفة البايتات الداخلية أبدًا. هذا الثبات مضمون فقط إذا تعذّر على الأصناف الفرعية إضافة حقول قابلة للتغيير. - String interning وتجمّع الثوابت. يُخزّن JVM حرفيات
Stringمؤقتًا. هذا التحسين آمن فقط لأن جميع السلاسل تتصرّف بالطريقة ذاتها؛ الأصناف الفرعية ستُفسد هذا الضمان.
Integer وLong وDouble وسائر أصناف التغليف البدائية final لأسباب الثبات والصحة ذاتها. وكذلك صنف Math.
الحقول final — تذكير سريع
رأيت مسبقًا final على الحقول (متغيّر لا يُسنَد إليه إلا مرة واحدة). هذا استخدام منفصل — لكنه مرتبط — للكلمة المفتاحية ذاتها. الاستخدامات الثلاثة تشترك في فلسفة واحدة: التزم بقيمة أو سلوك وامنع تغييره.
متى تستخدم final؟
قائمة تحقق ذهنية مفيدة:
- ضع final على التابع حين يُطبّق قاعدة أمنية، أو خطوة بروتوكول، أو خوارزمية تعتمد عليها الأصناف الفرعية لكنها لا يجب أن تُعدّلها.
- ضع final على الصنف حين يُمثّل نوع قيمة يجب أن يبقى ثابتًا (مثل
StringأوInteger)، أو حين يكون صنف أدوات مساعدة (مثلMath) لا يجب تهيئته عبر صنف فرعي. - لا تُفرط في استخدام final. جعل كل شيء
finalيجعل الكود أصعب في الاختبار (لا يمكن إنشاء بدائل للاختبار) وأصعب في التوسيع حين تتغيّر المتطلبات.
final على صنف لمنع الإساءة في استخدامه، ثم يكتشفون لاحقًا أن حالة استخدام مشروعة تحتاج إلى التوسيع. افضّل التصميم الجيد بمُحدّدات الوصول والتغليف أولًا. الجأ إلى final حين يكون لديك سبب وجيه — لا مجرد عادة.
التحقق وقت التصريف
final يُفحص بالكامل وقت التصريف — لا يوجد أي عبء وقت تشغيل. إذا حاولت توسيع صنف final أو إعادة تعريف تابع final، يرفض المُصرِّف الكود فورًا برسالة خطأ واضحة. هذا يجعله شبكة أمان بدون أي تكلفة.
الخلاصة
الكلمة المفتاحية final على تابع تمنع أي صنف فرعي من إعادة تعريف ذلك التابع، وتُثبّت سلوكًا بعينه. وعلى صنف تمنع توسيعه كليًا، وهكذا تضمن Java أن String وInteger وأنواع مشابهة تبقى ثابتة وجديرة بالثقة. استخدم final بوعي: لإنفاذ عقد، أو للحفاظ على الثبات، أو لحماية تنفيذ حساس أمنيًا.