الطرق الثابتة في Kotlin: كيف يمكن لمطور Java التنقل في عالم object و companion
\n\nإذا كنت تكتب بلغة Java وتبدأ للتو في تعلم Kotlin، فربما سألت نفسك: "أين static هنا؟". في Java، اعتدنا على كتابة public static void main() أو MathUtils.max() — إنها مريحة، بسيطة، ولا تتطلب إنشاء كائن. لكن في Kotlin، الكلمة المفتاحية static غير موجودة. إطلاقاً. تماماً.
لا داعي للذعر. لم يصاب مطورو Kotlin بالجنون — بل قرروا ببساطة أن الطرق الثابتة يمكن تنفيذها بشكل أكثر أناقة، بدون حالة عامة وبهندسة معمارية أوضح. في هذه المقالة، سنغطي جميع طرق استبدال static في Java بـ Kotlin: من الدوال البسيطة على مستوى الحزمة إلى companion object القوي. ستتعلم متى تستخدم كل منها وسترى أمثلة حية من الكود.
هيا بنا! 🚀
\n\n\n\n
لماذا لا يوجد static في Kotlin؟ (وهذا شيء جيد)
\n\nفي Java، static هو في الأساس متغير عام أو طريقة مرتبطة بالصنف، وليس بنسخة منه. إنه مناسب للأدوات المساعدة، لكنه ينتهك مبادئ OOP: لا يمكن توريث الأعضاء الثابتة، ويصعب اختبارها، وتخلق ارتباطات وثيقة.
يشجع Kotlin على نهج أنظف: الدوال والخصائص يمكن أن توجد على مستوى الحزمة (بدون صنف إطلاقاً!) أو يمكن تنظيمها في كائنات مفردة singleton. وإذا كنت بحاجة إلى ربط طريقة بصنف (كما في Java)، استخدم companion object. دعنا نفصل كل خيار.
\n\n
الطريقة 1: الدوال على مستوى الحزمة (الأبسط)
\n\nفي Kotlin، يمكنك تعريف دالة أو خاصية ببساطة في ملف، خارج أي صنف. هذا مشابه للطريقة الثابتة في Java، ولكن بدون الغلاف غير الضروري لصنف الأدوات المساعدة.
\n\nمثال:
\n\n// File: MathUtils.kt\npackage com.example.utils\n\nfun max(a: Int, b: Int): Int {\n return if (a > b) a else b\n}\n\nval PI = 3.14159 // package-level constant\n\nالآن يمكن استدعاء هذه الدالة من أي مكان (بعد الاستيراد):
\n\nimport com.example.utils.max\nimport com.example.utils.PI\n\nfun main() {\n println(max(10, 20)) // 20\n println(PI) // 3.14159\n}\n\nمتى تستخدم: للدوال والثوابت البسيطة التي لا تتطلب تجميعاً في صنف. هذه هي أسهل طريقة للحصول على سلوك "static".
\n\n\n\n
الطريقة 2: الكائن المفرد Singleton (لمجموعة من الدوال ذات الصلة)
\n\nإذا كنت بحاجة إلى تجميع عدة طرق ثابتة معاً (مثل MathUtils في Java)، استخدم object. هذا هو singleton — نسخة واحدة يتم إنشاؤها تلقائياً عند أول وصول.
مثال:
\n\nobject StringUtils {\n fun isEmpty(str: String?): Boolean {\n return str.isNullOrEmpty()\n }\n\n fun capitalizeFirst(str: String): String {\n return str.replaceFirstChar { it.uppercase() }\n }\n\n const val EMPTY_STRING = ""\n}\n\nالاستخدام:
\n\nfun main() {\n println(StringUtils.isEmpty(null)) // true\n println(StringUtils.capitalizeFirst("kotlin")) // Kotlin\n println(StringUtils.EMPTY_STRING) // ""\n}\n\nمهم: object هو صنف كامل. يمكنه تنفيذ واجهات، ولديه مهيئات، وحتى يمكنه الوراثة من أصناف أخرى. لكنه لا يمكن أن يحتوي على بناة (باستثناء الخاص).
\n\n
الطريقة 3: الكائن المرافق Companion Object (للطرق المرتبطة بصنف)
\n\nهذا هو أقرب تماثل لـ static في Java داخل صنف. companion object هو كائن مرافق يعيش داخل الصنف. يتم استدعاء طرقه وخصائصه عبر اسم الصنف، مثل الطرق الثابتة.
مثال:
\n\nclass User(val name: String, val age: Int) {\n companion