مقدمة: لماذا لا يريد data class أن يكون فارغًا؟
\n\nهل سبق لك أن حاولت إنشاء data class في Kotlin لتطبيق Android ووجدت بدهشة أن المترجم يرفض قبول المنشئ الفارغ؟ إذا كان الأمر كذلك، فأنت لست وحدك. هذا السؤال جمع آلاف المشاهدات على Stack Overflow، مما يعني أن مئات المطورين (من المبتدئين إلى الخبراء) تعثروا بنفس المشكلة.
لماذا يحدث هذا؟ السبب هو أن data class في Kotlin ليس مجرد فئة لتخزين البيانات. فهو يُنشئ تلقائيًا دوال equals() و hashCode() و toString() و copy() و componentN(). ولكل هذا السحر، يحتاج إلى منشئ أساسي (primary constructor) يحتوي على معامل واحد على الأقل. بدونه، لا يمكن.
في هذه المقالة، سنحلل أهم 5 أخطاء يرتكبها المطورون عند محاولة إنشاء منشئ فارغ لـ data class في Kotlin Android، وسنوضح كيفية تجنبها. هيا بنا!
الخطأ رقم 1: الإعلان المباشر عن منشئ فارغ
\n\nالخطأ الأكثر شيوعًا هو محاولة كتابة شيء مثل:
\n\n// ❌ هذا غير مسموح!\ndata class User {\n constructor() : this() // خطأ في الترجمة\n}\n\n\nيطلب Kotlin بشدة أن يتم الإعلان عن المنشئ الأساسي في رأس الفئة. إذا حاولت إنشاء منشئ ثانوي بدون معاملات يستدعي نفسه، فستحصل على استدعاء ذاتي لا نهائي و خطأ في الترجمة.
\n\nالحل: القيم الافتراضية
\n\nأبسط وأفضل طريقة هي تعيين قيم افتراضية لجميع معاملات المنشئ الأساسي:
\n\n// ✅ صحيح: قيم افتراضية\ndata class User(\n val name: String = "",\n val age: Int = 0,\n val email: String = ""\n)\n\n// الآن يمكنك إنشاء كائن بدون وسائط\nval emptyUser = User()\nprintln(emptyUser) // User(name=, age=0, email=)\n\n\nهذا الأسلوب يعمل "مباشرة" ولا يتطلب أي إضافات أو تعليقات توضيحية إضافية. تحصل على منشئ فارغ كامل، ويتم تهيئة جميع الحقول بالقيم الافتراضية.
\n\nالخطأ رقم 2: تجاهل متطلبات المكتبات (Room, Retrofit, Gson)
\n\nتتطلب العديد من مكتبات Android وجود منشئ فارغ لإنشاء الكائنات عبر الانعكاس (reflection). المثال الكلاسيكي هو Room:
\n\n// ❌ Room سيعطي خطأ\n@Entity\ndata class User(\n @PrimaryKey val id: Int,\n val name: String,\n val age: Int\n)\n\n\nعند محاولة استخدام data class مع Room، ستحصل على استثناء: Cannot instantiate User using no-arg constructor. تحدث مشاكل مماثلة مع Gson و Retrofit عند إلغاء تسلسل JSON.
الحل: القيم الافتراضية + الأنواع القابلة للصفر (nullable)
\n\nبالنسبة لـ Room، يكفي إضافة قيم افتراضية:
\n\n// ✅ Room سعيد\n@Entity\ndata class User(\n @PrimaryKey val id: Int = 0,\n val name: String = "",\n val age: Int = 0\n)\n\n\nبالنسبة لـ Gson و Retrofit، غالبًا ما يكون من الأسهل استخدام الأنواع القابلة للصفر:
\n\n// ✅ للعمل مع JSON\ndata class ApiResponse(\n val status: String? = null,\n val data: List<Item>? = null\n)\n\n\nولكن كن حذرًا: الأنواع القابلة للصفر قد تؤدي إلى استثناء المؤشر الفارغ (NPE) إذا نسيت التحقق من القيمة. استخدمها بوعي.
\n\nالخطأ رقم 3: نسيان إضافة إضافة Kotlin No-Arg
\n\nأحيانًا لا تكون القيم الافتراضية مناسبة. على سبيل المثال، إذا كنت تستخدم Jackson للتسلسل وتحتاج إلى أنواع محددة بدون nullable. في هذه الحالة، تأتي إضافة kotlin-noarg للإنقاذ.
يعتقد العديد من المطورين خطأً أنه يكفي فقط إضافة التبعية في ملف build.gradle. ولكن بدون الإعداد الصحيح، لن تعمل الإضافة.
الحل: الإعداد الصحيح لـ No-Arg
\n\nأضف الإضافة في ملف build.gradle.kt