Public get, private set في Kotlin: كيف تتجنب الأخطاء الشائعة (وما الحل إذا وقعت فيها)
\n\nمرحبًا، زميلي المطور! إذا كنت قد بحثت يومًا عن "Kotlin public get private set var"، فأنت لست وحدك. هذا الاستعلام من أكثر الأسئلة شيوعًا على Stack Overflow، وقد اصطدم آلاف المطورين (بما فيهم أنا سابقًا) بهذه المشكلة محاولين فهم لماذا لا يعمل الكود الخاص بهم.
\n\nقد يبدو الأمر بسيطًا: تريد أن تكون الخاصية متاحة للجميع (public get)، ولكن فقط الفئة نفسها هي التي يمكنها تعديلها (private set). في Java، كان هذا روتينًا: تعلن عن حقل خاص (private)، وتكتب دالة getter و setter، ثم تجعل setter خاصًا. أما في Kotlin، فمن المفترض أن يكون الأمر أسهل، ولكن...
\n\nالمفاجأة: الصيغة موجودة، لكنها لا تعمل دائمًا كما تتوقع. في هذا المقال، سنستعرض أهم الأخطاء التي يقع فيها حتى المطورون المحترفون، ونوضح كيفية تجنبها. هيا بنا!
\n\nتشريح الخاصية: var, val, get و set
\n\nبدايةً، شرح سريع. في Kotlin، الخصائص (properties) ليست مجرد حقول. إنها عناصر كاملة يمكنها احتواء دوال getter و setter. إليك البنية الأساسية:
\n\nvar اسم_الخاصية: النوع = القيمة_الافتراضية\n get() = field\n set(value) { field = value }\n\nعند استخدام var، يقوم Kotlin تلقائيًا بإنشاء دالتي getter و setter. لكن يمكنك تغيير سلوكهما. والأهم، يمكنك تغيير مدى رؤيتهما (visibility modifiers: public, private, internal, protected).
وهنا يبدأ الجزء المثير. لجعل getter عامًا (public) و setter خاصًا (private)، اكتب:
\n\nvar name: String = "default"\n private set\n\nهل هذا كل شيء؟ تقريبًا. لكن هناك تفاصيل دقيقة تعطل هذه الآلية.
\n\nالخطأ رقم 1: نسيان المُنشئ (Constructor)
\n\nالمشكلة الأكثر شيوعًا هي محاولة تعريف public get private set var داخل مُنشئ الفئة. على سبيل المثال:
// ❌ خطأ: هذا غير مسموح\nclass User(public get private set var name: String)\n\nسيظهر خطأ في الترجمة. لماذا؟ لأن معاملات المُنشئ ليست خصائص. إنها مجرد معاملات. إذا كنت تريد خاصية مع تغيير مدى رؤية setter، يجب تعريفها داخل جسم الفئة.
\n\nالطريقة الصحيحة:
\n\nclass User(name: String) {\n var name: String = name\n private set\n}\n\nأو، إذا كنت تستخدم data class، ستحتاج إلى حل بديل:
data class User(private val _name: String) {\n var name: String = _name\n private set\n}\n\nنعم، data class لا يسمح بتغيير مدى رؤية setter مباشرةً للخصائص القادمة من المُنشئ الأساسي. ستحتاج إلى استخدام حقل داعم (backing field).
الخطأ رقم 2: الخلط مع الحقل الداعم (Backing Field)
\n\nالحقل الداعم هو حقل مخفي يقوم Kotlin بإنشائه تلقائيًا للخاصية إذا استخدمت field في getter أو setter. ولكن إذا قمت بإعادة تعريف getter أو setter دون استخدام field، فلن يتم إنشاء الحقل الداعم.
مثال على المشكلة:
\n\nvar counter: Int = 0\n private set\n get() = field * 2 // ✅ هذا صحيح، field موجود\n\nلكن إذا كتبت:
\n\nvar counter: Int = 0\n private set\n get() = 42 // هذا صحيح، لكن field غير مستخدم\n\nفي هذه الحالة، لن يتم إنشاء الحقل الداعم. وإذا حاولت استخدام field في setter، ستحصل على خطأ. الخبر الجيد: عند استخدام private set، يتم إنشاء الحقل الداعم تلقائيًا في معظم الحالات لأن setter الافتراضي يستخدم field. لكن إذا قمت بإعادة تعريف setter دون field، فاستعد للمفاجآت.
نصيحة: استخدم دائمًا field بشكل صريح في getter و setter إذا كنت تريد العمل مع الحقل الداعم. وتذكر: إذا لم يكن للخاصية حقل داعم