أهلاً بكم يا طلابي الأعزاء! لقد أنهينا للتو دراسة الأصناف (Classes) والكائنات (Objects). الآن تغيّر تفكيرنا — انتقلنا من التفكير الإجرائي (Procedural Thinking) إلى التفكير كائني التوجه (Object-Oriented Thinking). أصبحنا نستطيع التعرف على الكائنات (Objects)، وخصائصها (الحقول — Fields)، وسلوكها (الدوال — Methods)، والتعبير عنها بمخططات UML، وترجمة تلك المخططات إلى كود Java.
قبل أن نبدأ المادة الجديدة، لنسترجع سريعاً الركيزتين الأساسيتين في البرمجة كائنية التوجه اللتين تعرفنا عليهما من قبل.
private، ولا يتعامل الخارج معها إلا من خلال دوال محددة public — دوال الإرجاع (Getters) ودوال التعيين (Setters).تخيّلها كالكبسولة الدوائية — المحتوى مخفي في الداخل؛ تتعامل فقط مع الغلاف الخارجي.
كقيادة السيارة: تستخدم المقود والدواسات دون معرفة تفاصيل المحرك الداخلية.
كل كائن (Object) ننشئه إما قابل للتغيير (Mutable) أو غير قابل للتغيير (Immutable). سنستعرض نقاشاً داخل الفصل خطوة بخطوة — مع كود Java الفعلي في كل مرحلة حتى ترى بالضبط ما الذي يتغير.
StringBuilder) قابلة للتغيير (Mutable) بشكل افتراضي.String. بمجرد إنشاء الـString، تبقى أحرفه ثابتة.Student — يحتوي على id وname وage. له باني (Constructor) واحد، ودوال إرجاع (Getters)، ودوال تعيين (Setters). سأنشئ كائناً وأتخيله في الذاكرة. سؤالي: هل هذا الكائن قابل للتغيير (Mutable) أم غير قابل للتغيير (Immutable)؟ هل يمكننا تغيير محتواه بعد الإنشاء؟📦 الكائن في الذاكرة — قبل وبعد setAge(21):
main. هل لا يزال بإمكاننا تغيير الكائن؟publicprivate إلى public؟ لا تزال لا توجد دوال تعيين (Setters). هل لا يزال الكائن غير قابل للتغيير (Immutable)؟public، يمكن لأي كود كتابة ali.age = 99 مباشرةً — دون الحاجة إلى Setter! يجب أن تبقى الحقول private دائماً!private، ولا يجب وجود أي دوال تعيين (Setters).privateثم أضاف الأستاذ مصفوفة درجات (marks) إلى صنف Student، وأثبت أنه حتى مع تحقق القاعدتين، لا يزال من الممكن تعديل الكائن. وهذا يقودنا إلى اكتشاف القاعدة الثالثة والأكثر إدهاشاً.
marks، وهو مصفوفة (Array) من نوع int[]. لا توجد دوال تعيين (Setters)، والحقل private. أضفت دالة إرجاع (Getter) تُعيد الدرجات. سؤالي — هل هذا الصنف غير قابل للتغيير (Immutable)؟private، ولا توجد دوال تعيين (Setters). يجب أن يكون غير قابل للتغيير (Immutable)!getMarks() وأخزّن النتيجة في متغير مؤقت. بعدها سأغيّر العنصر الثاني من خلال ذلك المتغير. لنطبع الدرجات قبل وبعد…تغيّرت درجة الطالب من 85 → 0، رغم أنه لا توجد دوال تعيين (Setters) وجميع الحقول private. لأن getMarks() أعادت المرجع (Reference) الفعلي للمصفوفة الداخلية. استخدم المُستدعي ذلك المرجع ليكتب مباشرة في ذاكرة المصفوفة.
private، ولا توجد دوال تعيين (Setters). لكن الكائن عُدِّل رغم ذلك! ما رأيكم؟ ماذا ينقصنا؟ ما القاعدة الثالثة؟getName() أيضاً تُعيد مرجعاً (Reference) للـString. هل هذه مشكلة أيضاً؟getName() تُعيد String. الآن — هل String قابل للتغيير (Mutable) أم غير قابل للتغيير (Immutable)؟String غير قابل للتغيير (Immutable)! بمجرد إنشائه، لا يمكن تغيير أحرفه.getName() المرجع (Reference) للـString الداخلي، فالمُستدعي لا يستطيع تغيير قيمته — لأن String لا يملك دوالاً لتعديل أحرفه. آمن للإعادة. ✅لكن الآن — ماذا لو غيّرت نوع
name من String إلى StringBuilder؟ الـStringBuilder قابل للتغيير (Mutable) — له دوال مثل .replace() و.delete(). لو أعادت getName() مرجعاً (Reference) لـStringBuilder، يمكن للمُستدعي استدعاء تلك الدوال وتغيير الاسم — رغم عدم وجود Setter!StringBuilder، أو أي صنف يمكن تعديله بعد الإنشاء!String، int، double) — يمكن إعادته مباشرة بأمان.Student الذي يحتوي على مصفوفة الدرجات (marks)؟ نعلم أننا لا نستطيع حذف دالة الإرجاع (Getter) — الطلاب يحتاجون لقراءة درجاتهم. إذن ما الحل؟الكائن (Object) غير قابل للتغيير (Immutable) عندما لا يمكن تغيير محتواه بعد إنشائه. أي صنف يحقق القواعد التالية كلها — أي كائن يُنشأ منه يكون غير قابل للتغيير (Immutable):
privatefinal على المصفوفة (Array)؟كلمة final على متغير مرجع (Reference Variable) تعني أن المرجع نفسه لا يمكن إعادة تعيينه. لكنها لا تمنع تعديل محتويات المصفوفة — فـmarks[1] = 0 لا تزال تُترجم وتُنفَّذ بنجاح. لذا كلمة final وحدها لا تكفي — ما زلت بحاجة إلى النسخة الواقية (Defensive Copy) في دالة الإرجاع (Getter)!