الثوابت في Kotlin: توقف عن التخمين، ابدأ باستخدامها بشكل صحيح
\n\nإذا كنت تكتب بلغة Kotlin لمدة أسبوع على الأقل، فمن المحتمل أنك واجهت السؤال: "كيف أعلن عن ثابت بشكل صحيح؟" يبدو الأمر بسيطًا — هناك val، وهناك const val، وهناك companion object. لكن الشيطان، كما هو الحال دائمًا، يكمن في التفاصيل. وهذا السؤال على Stack Overflow حصد آلاف المشاهدات ليس من دون سبب.
دعنا ننهي الأمر مرة واحدة وإلى الأبد: ما هي طرق إنشاء الثوابت في Kotlin، ومتى تستخدم أيًا منها، ولماذا val ليس دائمًا ثابتًا. هيا بنا!
1. val مقابل const val: أين المكمن؟
\n\nالسؤال الأكثر شيوعًا للمبتدئين: "لقد كتبت val — هل هذا ثابت؟" الإجابة: نعم ولا.
val هو مرجع غير قابل للتغيير (للقراءة فقط). لكن القيمة تُحسب في وقت التشغيل. أي أنه ليس ثابتًا في وقت الترجمة.
// هذا ليس ثابتًا في وقت الترجمة\nval currentTime = System.currentTimeMillis()\n\nconst val هو ثابت حقيقي يُدمج في الكود في مرحلة الترجمة (مثل static final في Java).
// أما هذا فهو ثابت\nconst val PI = 3.14159\n\nقيود مهمة: يمكن استخدام const val فقط مع الأنواع البدائية و String. لا يُسمح بالكائنات أو القوائم أو الفئات المخصصة.
💡 القاعدة الذهبية: إذا كانت القيمة معروفة في وقت الترجمة — استخدم const val. إذا كانت تُحسب في وقت التشغيل — استخدم val.
2. أين تعلن الثوابت؟ أفضل 3 طرق
\n\nالطريقة 1: ثوابت المستوى الأعلى (موصى بها)
\n\nأبسط وأنظف طريقة — أعلن الثابت في المستوى الأعلى للملف. لا حاجة للفئات، ولا للكائنات — مجرد ملف بالثوابت.
\n\n// Constants.kt\nconst val BASE_URL = "https://api.example.com"\nconst val TIMEOUT_SECONDS = 30\nconst val APP_NAME = "MyKotlinApp"\n\nلماذا هذا رائع؟
\n- \n
- أقل كود ممكن \n
- متاح من أي مكان بدون بادئات \n
- يقوم المترجم بدمج القيم \n
- سهل إعادة الهيكلة \n
الطريقة 2: companion object داخل الفئة
\n\nالنهج الكلاسيكي من Java الذي انتقل إلى Kotlin. استخدمه عندما تكون الثوابت مرتبطة منطقيًا بالفئة.
\n\nclass NetworkClient {\n companion object {\n const val DEFAULT_PORT = 8080\n const val PROTOCOL = "https"\n }\n}\n\n// الاستخدام\nval port = NetworkClient.DEFAULT_PORT\n\nالعيب: ضجيج نحوي. الميزة: الثوابت مرتبطة بالسياق.
\n\nالطريقة 3: object (مفرد) لمجموعات الثوابت
\n\nعندما يكون هناك العديد من الثوابت وتحتاج إلى تجميعها حسب المعنى.
\n\nobject AppConstants {\n const val MAX_LOGIN_ATTEMPTS = 5\n const val SESSION_DURATION = 3600L\n const val DATE_FORMAT = "yyyy-MM-dd"\n\n object Urls {\n const val LOGIN = "/auth/login"\n const val LOGOUT = "/auth/logout"\n }\n}\n\n// الاستخدام\nAppConstants.Urls.LOGIN\n\n3. الثوابت في Android: الخصائص
\n\nإذا كنت تكتب لتطبيقات Android، فهناك بعض التفاصيل الإضافية.
\n\nالثوابت في companion object و @JvmStatic
\n\nلجعل الثابت من companion object ثابتًا حقيقيًا في Java (على سبيل المثال، للتعليقات التوضيحية)، استخدم @JvmStatic:
class MyFragment : Fragment() {\n companion object {\n @JvmStatic\n const val ARG_ID = "id"\n }\n}\n\nالثوابت في الموارد
\n\nلا تنسَ الطريقة الكلاسيكية في Android — الموارد. للنصوص والألوان والأحجام:
\n\n// strings.xml\n<string name="app_name">MyApp</string>\n\n// في الكود\nval appName = getString(R.string.app_name)\n\nلكن هذه ليست ثوابت وقت الترجمة.