ما هي "الفئة الثابتة" في Java؟ نشرحها بالأمثلة
\n\nإذا كنت قد كتبت كودًا بلغة Java من قبل، فبالتأكيد صادفت البناء الغامض static class. "كيف ذلك؟" قد تسأل. "فإن static هو معدّل للحقول والطرق، وليس للفئات!" وستكون على حق... تقريبًا.
في الواقع، لا توجد "فئات ثابتة" في Java بنفس المعنى الذي توجد به متغيرات أو طرق ثابتة. ولكن هناك فئات متداخلة ثابتة (static nested classes). وهذه واحدة من أكثر الإمكانيات غير المقدرة ولكنها مفيدة جدًا في اللغة. دعنا نفهم ما هو هذا الكائن، وكيف يعمل تحت غطاء JVM، ولماذا حصد هذا السؤال آلاف المشاهدات على Stack Overflow.
\n\nإجابة مختصرة (للمستعجلين)
\n\nالفئة المتداخلة الثابتة هي فئة تم تعريفها داخل فئة أخرى باستخدام الكلمة المفتاحية static. على عكس الفئة الداخلية (inner class)، فهي لا تخزن مرجعًا إلى مثيل الفئة الخارجية. في جوهرها، هي فئة عادية "تعيش" داخل فئة أخرى للتجميع المنطقي.
public class خارجي {\n private static String سر = "أنا حقل static";\n\n // فئة متداخلة ثابتة\n public static class داخليثابت {\n public void اظهرالسر() {\n // يمكنها الوصول فقط إلى الأعضاء static للفئة الخارجية\n System.out.println(سر);\n }\n }\n}\n\n// الاستخدام\nخارجي.داخليثابت كائن = new خارجي.داخليثابت();\nكائن.اظهرالسر(); // سيُطبع: أنا حقل static\n\nبسيط؟ نعم. لكن الشيطان، كما هو الحال دائمًا، في التفاصيل. دعنا نتعمق أكثر.
\n\nكيف يعمل هذا تحت غطاء JVM؟
\n\nعندما يواجه المترجم فئة متداخلة ثابتة، يقوم بأمرين مهمين:
\n\n- \n
- إنشاء ملف .class منفصل باسم
خارجي$داخليثابت.class. نعم، علامة الدولار ليست صدفة، بل هي اتفاقية قياسية في JVM. \n - عدم إضافة مرجع مخفي إلى مثيل الفئة الخارجية. هذا هو الفرق الرئيسي عن الفئات الداخلية غير الثابتة. \n
دعنا نلقي نظرة "تحت الغطاء" باستخدام مفكك التجميع. لنأخذ مثالنا أعلاه ونرى ما يحدث بعد التجميع:
\n\n// كود بايت تقريبي (مبسط)\npublic class خارجي$داخليثابت {\n public void اظهرالسر() {\n System.out.println(خارجي.سر); // وصول مباشر إلى الحقل static\n }\n}\n\nلاحظ: لا يوجد this$0 (مرجع مخفي للفئة الخارجية) هنا. الآن قارن مع الفئة الداخلية العادية:
public class خارجي {\n class داخليعادي {\n public void طريقة() {\n // يخزن ضمنيًا مرجع this$0 إلى مثيل خارجي\n }\n }\n}\n\nفي كود البايت للفئة الداخلية العادية، يضيف المترجم تلقائيًا حقل final Outer this$0. هذا يعني أنه لا يمكن إنشاء مثل هذه الفئة دون وجود مثيل موجود للفئة الخارجية. أما الفئة المتداخلة الثابتة، فهي سيدة نفسها.
متى تستخدم الفئة المتداخلة الثابتة؟ 3 سيناريوهات حقيقية
\n\n1. نمط Builder (الباني)
\n\nالمثال الأكثر كلاسيكية هو نمط Builder. إذا كنت قد كتبت يومًا new StringBuilder().append(...)، فقد استخدمت ثمار الفئات المتداخلة الثابتة. ألق نظرة على مثال حقيقي من المكتبة القياسية لـ Java:
public class Person {\n private final String name;\n private final int age;\n private final String email;\n\n private Person(Builder builder) {\n this.name = builder.name;\n this.age = builder.age;\n this.email = builder.email;\n }\n\n // فئة متداخلة ثابتة Builder\n public static class Builder {\n private String name;\n private int age;\n private String email;\n\n public Builder setN