كائن الرفيق (Companion object) في Kotlin: بديل static في Java والأداء

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا لا يوجد static في Kotlin ولكن يوجد companion object؟ نفهم الأداء والمنطق



إذا كنت تنتقل من Java إلى Kotlin، فإن أحد الأسئلة الأولى التي ستطرحها على StackOverflow (والتي جمعت بالفعل آلاف المشاهدات) هو: «لماذا نستخدم companion object بدلاً من Java static؟ وهل يؤدي ذلك إلى إبطاء التطبيق؟»



لنكن صادقين: عندما رأيت بنية companion object لأول مرة، بدا لي أن فريق Kotlin قرر فقط تعقيد الحياة. لماذا نكتب كودًا أكثر بينما كان بإمكاننا ببساطة نسخ static من Java؟ ولكن بعد التعمق، أدركت: هذا ليس نزوة، بل قرار معماري مدروس يجعل الكود أنظف وأكثر أمانًا... ونعم، أحيانًا أسرع.



في هذه المقالة، لن نترجم إجابة من SO فقط. سنشرح كيف يعمل companion object تحت الغطاء، ونقارن أداءه مع Java static، ونوضح متى نستخدمه ومتى نختار مسارًا آخر.



ما هو companion object بكلمات بسيطة؟



في Java، تنتمي الحقول والطرق الثابتة إلى الفئة وليس إلى الكائن. في Kotlin، قرروا الابتعاد عن الكلمة المفتاحية static لأنها تنتهك مبادئ البرمجة الشيئية (لا يمكن إعادة تعريف الثابت، وهو ليس جزءًا من الكائن). بدلاً من ذلك، يقدم Kotlin كائنات الرفيق.



تخيل أن لديك فئة User. تريد تخزين ثابت MAX_AGE وطريقة مصنع createGuest(). في Java ستكتب:



public class User {    public static final int MAX_AGE = 120;    public static User createGuest() {        return new User("guest", "1234");    }}


في Kotlin يبدو الأمر هكذا:



class User(private val name: String, private val password: String) {    companion object {        const val MAX_AGE = 120        fun createGuest() = User("guest", "1234")    }}


لاحظ: companion object ليس مجرد «كتلة static». إنه كائن كامل يعيش داخل فئتك. يمكن أن يحتوي على واجهات وإضافات وحتى اسم خاص به (إذا قمت بتعيينه).



كيف يعمل companion object تحت الغطاء؟ (من منظور JVM)



لفهم الأداء، دعنا نلقي نظرة على البايت كود. عندما تكتب companion object، يقوم مترجم Kotlin بما يلي:



  • ينشئ فئة ثابتة داخلية باسم Companion (أو اسم مخصص لك).
  • يضع جميع الحقول والطرق من companion object في هذه الفئة.
  • إذا كنت تستخدم التعليق التوضيحي @JvmStatic، يقوم المترجم بالإضافة إلى ذلك بإنشاء طرق ثابتة غلافية في الفئة نفسها.


مثال مع التعليق التوضيحي @JvmStatic:



class User {    companion object {        @JvmStatic        fun createGuest() = User()    }}


الآن من Java يمكنك استدعاء User.createGuest() كطريقة ثابتة عادية. بدون هذا التعليق التوضيحي، كان عليك كتابة User.Companion.createGuest().



الاستنتاج للأداء: إذا كنت تعمل فقط مع Kotlin، فلا توجد تكاليف إضافية. إذا كنت تستدعي من Java، فهناك مستوى واحد إضافي من عدم المباشرة (عبر كائن Companion). لكن مترجم JIT في JVM يقوم بتحسين هذه الاستدعاءات بشكل ممتاز، لذا فإن الفرق في السرعة يقترب من الصفر.



الأداء: companion object مقابل Java static



دعنا نبدد الأسطورة الرئيسية: companion object لا يبطئ. لقد أجريت عدة اختبارات أداء صغيرة (على Kotlin 1.9, JVM 17)، وهذه هي النتائج:



المدونات

توصيات الكتب

السيناريو Java static