لماذا "أخذ" منا Kotlin add و remove و put؟ نفهم الأمر مرة واحدة وإلى الأبد
\n\nإذا كنت قد كتبت بلغة Java من قبل، ثم انتقلت إلى Kotlin، فمن المحتمل أنك واجهت هذه اللحظة من الحيرة الطفيفة. تقوم بإنشاء List، وتريد إضافة عنصر إليه - تكتب .add()، وتقوم بيئة التطوير بتسليط الضوء عليه باللون الأحمر. أو تحاول وضع قيمة في Map باستخدام .put() - وتتلقى خطأ مرة أخرى.
هل هذا مألوف؟ بالتأكيد! السؤال "Kotlin's List missing "add", "remove", Map missing "put", etc?" على Stack Overflow حصد آلاف المشاهدات وعشرات التعليقات. إنها حجر عثرة حقيقي للمبتدئين، وحتى المطورين ذوي الخبرة يجدون أحيانًا أنفسهم يفكرون: "أين طرقاتي المعتادة؟"
\n\nفي هذه المقالة، لن نجيب على هذا السؤال فحسب. بل سنوضح كيف تحول فلسفة Kotlin هذا "النقص" إلى ميزتك الرئيسية. ستتعلم أفضل الممارسات للعمل مع المجموعات في عام 2025، وستتعلم اختيار النوع الصحيح من المجموعة، وستتوقف عن الخلط بين listOf و mutableListOf. هيا بنا!
ما الجوهر؟ الثبات كأسلوب حياة
\n\nأول شيء يجب فهمه: تم تصميم Kotlin منذ البداية كلغة تشجع على الكود الآمن والقابل للتنبؤ. أحد المبادئ الرئيسية هو الثبات (immutability) بشكل افتراضي.
\n\nفي Java، لديك واجهة واحدة List، وهي تحتوي دائمًا على طرق add() و remove() و set() وهكذا. حتى إذا قمت بإنشاء قائمة باستخدام List.of() (Java 9+)، فلا يزال لديك إمكانية الوصول إلى هذه الطرق - فهي تطرح استثناءً فقط أثناء التنفيذ. هذا غير مريح وغير آمن.
اتبع Kotlin مسارًا مختلفًا. قام بتقسيم المجموعات إلى نوعين:
\n\n- \n
- غير قابلة للتغيير (للقراءة فقط):
List،Set،Map- لا تحتوي على طرق تغير المحتوى. \n - قابلة للتغيير (mutable):
MutableList،MutableSet،MutableMap- هذه هي التي تحتوي على جميع الطرق المألوفةaddوremoveوput. \n
لهذا السبب بالتحديد، listOf(1, 2, 3) لا تحتوي على طريقة .add(). إنها تُرجع قائمة غير قابلة للتغيير. بينما mutableListOf(1, 2, 3) - تُرجع قائمة قابلة للتغيير، وتحتوي على .add().
يبدو هذا منطقيًا؟ دعنا نلقي نظرة على الكود.
\n\nأمثلة: ما يعمل وما لا يعمل
\n\nإليك موقف كلاسيكي يربك المبتدئين:
\n\n// ❌ خطأ! List لا تحتوي على add()\nval fruits = listOf("apple", "banana")\nfruits.add("orange") // خطأ في الترجمة\n\n// ✅ صحيح: استخدم MutableList\nval mutableFruits = mutableListOf("apple", "banana")\nmutableFruits.add("orange") // الآن يعمل!\nprintln(mutableFruits) // [apple, banana, orange]\n\n\nنفس القصة مع Map:
\n\n// ❌ خطأ! Map لا تحتوي على put()\nval capitals = mapOf("Russia" to "Moscow", "France" to "Paris")\ncapitals.put("Germany", "Berlin") // خطأ في الترجمة\n\n// ✅ صحيح: استخدم MutableMap\nval mutableCapitals = mutableMapOf("Russia" to "Moscow", "France" to "Paris")\nmutableCapitals.put("Germany", "Berlin")\n// أو بشكل أقصر:\nmutableCapitals["Germany"] = "Berlin"\nprintln(mutableCapitals) // {Russia=Moscow, France=Paris, Germany=Berlin}\n\n\nلاحظ: بالنسبة لـ MutableMap، يمكنك استخدام الأقواس التشغيلية [] - وهذا هو put() تحت الغطاء. مريح جدًا!
ولكن لماذا؟ لماذا يعقد Kotlin الحياة بهذا الشكل؟
\n\nللوهلة الأولى، يبدو أن هذا صداع إضافي غير ضروري. لماذا يجب حفظ نوعين من المجموعات؟ في الواقع، هذا حل عبقري. إليك ثلاثة أسباب رئيسية:
\n\n1. الأمان على مستوى الأنواع
\nيقوم المترجم الآن بمراقبة عدم قيامك بتغيير قائمة عن طريق الخطأ والتي من المفترض أن تبقى