من يحق له لمس شيفرتك
محدد الوصول كلمة مفتاحية توضع أمام صنف أو حقل أو دالة أو باني، وتقرر من غيرك يُسمح له باستخدامه. وهو أساس التغليف: تُخفي الأحشاء المتشابكة لصنفٍ ما وتكشف فقط الأجزاء التي تثق بأن تستدعيها الشيفرة الأخرى.
في جافا أربعة مستويات، من الأكثر انفتاحًا إلى الأكثر انغلاقًا: public وprotected وdefault (بلا أي كلمة مفتاحية على الإطلاق) وprivate. إن اخترتها بإحكام صار الصنف آمنًا للتعديل لاحقًا، لأن لا شيء في الخارج يعتمد على تفاصيل لم تَعِد قط بالحفاظ عليها.
private: مرئي داخل الصنف فقط
private هو أشد المستويات إحكامًا. لا يستطيع لمس عنصرٍ private إلا شيفرة مكتوبة داخل الصنف نفسه - لا الأصناف الفرعية، ولا الأصناف الأخرى في الحزمة نفسها، لا أحد. هنا يكمن مكان أغلب حقولك.
balance محدد بـ private، فالطريقة الوحيدة لتغييره هي عبر deposit الذي يرفض المبالغ السالبة. لو كان balance بـ public لاستطاعت أي شيفرة أن تكتب account.balance = -9999 وتتخطى الفحص كليًا. هذا الفحص هو الغاية كلها من إخفاء الحقل.
جرّب إزالة التعليق عن account.balance = 500; في main فستحصل على خطأ في الترجمة: balance has private access in BankAccount.
public: مرئي في كل مكان
public هو النقيض الأقصى - أي أحد، في أي مكان، يستطيع استخدام العنصر. أسماء دوالك التي يُقصد أن تستدعيها الشيفرة الأخرى تكون عادةً public. وكذلك الصنف نفسه حين يلزم الوصول إليه من حزمٍ أخرى.
النمط الشائع - الذي يُسمى أحيانًا الواجهة العامة للصنف - هو: حقول خاصة، دوال عامة. تحمل الحقول حالةً ينبغي ألا يعبث بها أحد سواك؛ والدوال هي الأبواب المضبوطة للدخول والخروج. getBalance وdeposit أعلاه هما تحديدًا هذا.
default (package-private): بلا أي كلمة مفتاحية
إن لم تكتب أي محدد فستحصل على وصول default، ويُسمى أيضًا package-private. يكون العنصر مرئيًا لكل صنف في الحزمة نفسها وغير مرئي لكل ما عداها. لا توجد كلمة مفتاحية لهذا المستوى - غياب الكلمة هو المستوى ذاته.
class Invoice { // بلا محدد -> مرئي داخل هذه الحزمة فقط
int amount; // بلا محدد -> حقل package-private
}
هذا يوقع كثيرًا من المبتدئين: إغفال public لا يجعل الشيء private، بل يجعله package-private. فالحقل الذي قصدت إبقاءه سرًا يظل قابلًا للقراءة والكتابة تمامًا من أي صنف يصادف وجوده في الحزمة نفسها. إن أردته مخفيًا حقًا فعليك كتابة private صراحةً.
وصول default مفيد فعلًا للأصناف المساعِدة التي تُعد تفصيلًا داخليًا لحزمةٍ ما - فأنت لا تصدّرها، لكن الأصناف المتعاونة داخل الحزمة تظل قادرة على استخدامها بحرية.
protected: الحزمة إضافة إلى الأصناف الفرعية
protected أكثر تساهلًا بدرجة واحدة من default. العنصر protected مرئي في كل مكان يكون default فيه مرئيًا (الحزمة نفسها)، وكذلك للأصناف الفرعية - حتى الأصناف الفرعية في حزمة مختلفة. وهو مُصمَّم لأشياء تريد مشاركتها مع الأصناف التي توسّع صنفك، لا مع العالم بأسره.
يصل Dog إلى name وsound() لأنهما protected ولأن Dog صنف فرعي. أما الشيفرة الخارجية التي ليست صنفًا فرعيًا وليست في الحزمة فلا تزال عاجزة عن لمسهما. لُذ بـ protected حين تصمم صنفًا مقصودًا للتوسيع؛ وإلا ففضّل private.
المستويات الأربعة في لمحة
إليك الصورة الكاملة، من الأكثر تقييدًا إلى الأكثر انفتاحًا:
| المحدد | الصنف نفسه | الحزمة نفسها | صنف فرعي (حزمة أخرى) | كل مكان |
|---|---|---|---|---|
private | نعم | لا | لا | لا |
| default | نعم | نعم | لا | لا |
protected | نعم | نعم | نعم | لا |
public | نعم | نعم | نعم | نعم |
قاعدة عملية بسيطة: ابدأ بـ private ولا تخفّف القيد إلا حين يحتاج شيء ما فعلًا إلى وصول أوسع. فتوسيع الوصول لاحقًا أيسر بكثير من تضييقه بعد أن تكون شيفرة أخرى قد بدأت تعتمد على عنصرٍ ما.
المحددات على الأصناف نفسها
تنطبق محددات الوصول على الأصناف أيضًا، لا على الأعضاء فحسب - لكن الصنف الأعلى مستوى (غير المتداخل داخل صنف آخر) لا يمكن أن يكون إلا public أو default. لا يمكنك كتابة صنف أعلى مستوى بـ private أو protected.
public class Order { ... } // يمكن الوصول إليه من أي حزمة
class LineItem { ... } // default - هذه الحزمة وحدها تراه
يجب أن يكون الصنف الأعلى مستوى public في ملف يحمل اسمه (Order.java). أما الأصناف المتداخلة فتستطيع استخدام المحددات الأربعة جميعها - ولهذا استطاعت الأمثلة أعلاه أن تجعل الأصناف الداخلية private أو default. نقطة دقيقة: الدالة public التي تُرجِع نوعًا package-private ليست مفيدة كثيرًا للمستدعين الخارجيين، لذا حافظ على اتساق واجهتك العامة.
التالي: الأعضاء الساكنة
حتى الآن كان كل حقل وكل دالة هنا ينتمي إلى نسخة - تنشئ كائنًا بـ new ثم تستدعي عليه الدوال. لكن أحيانًا تنتمي قيمة أو سلوك إلى الصنف ككل، مشتركةً بين جميع النسخ، مثل عدّاد يحصي كم كائنًا موجودًا. لذلك وُجدت الكلمة المفتاحية static، وهي ما سنتناوله تاليًا.
الأسئلة الشائعة
ما هي محددات الوصول الأربعة في جافا؟
public وprivate وprotected والـ default (بلا كلمة مفتاحية، ويُسمى أيضًا package-private). public مرئي في كل مكان، وprivate داخل الصنف نفسه فقط، وprotected داخل الحزمة إضافة إلى الأصناف الفرعية، والـ default داخل الحزمة نفسها فقط.
ما الفرق بين public وprivate في جافا؟
public يعني أن أي شيفرة في أي مكان تستطيع الوصول إلى العنصر. أما private فيعني أن الشيفرة داخل الصنف نفسه فقط هي التي تستطيع ذلك - فالأصناف الفرعية والأصناف الأخرى لا تستطيع حتى رؤيته. عادةً تجعل الحقول private وتكشفها عبر دوال public.
ماذا يعني ألا يكون لحقل في جافا محدد وصول؟
غياب الكلمة المفتاحية يعني وصول default (package-private): العنصر مرئي لكل صنف في الحزمة نفسها، لكنه غير مرئي خارجها. وهو أصرم من public لكنه أكثر تساهلًا من private - ومن السهل إغفاله سهوًا، لذا كن واعيًا في التعامل معه.