كيفية اجتياز مقابلة Kotlin: أهم الأسئلة المتقدمة
\n\nمقابلة مطور Kotlin لا تقتصر فقط على اختبار بناء الجملة. على المستوى المتقدم، يريد المحاورون رؤية فهم عميق للغة وخصائصها والقدرة على كتابة كود فعال وموجز وآمن. في هذه المقالة، سنحلل المواضيع الرئيسية التي تظهر غالبًا في المقابلات التقنية: coroutines، الدوال المضمنة (inline functions)، الفئات المختومة (Sealed classes)، التفويض (delegation)، وخبايا تعبيرات lambda. تم إعداد المحتوى للمنصة التعليمية PythonLib، حيث نساعد المطورين على تنظيم معرفتهم.
\n\n1. الكوروتينات (Coroutines) والتزامن المنظم (Structured Concurrency)
\nالكوروتينات هي أساس البرمجة غير المتزامنة في Kotlin. الأسئلة حول هذا الموضوع تظهر ما إذا كنت تفهم كيفية عمل المجدول (scheduler)، السياق (context)، ومعالجة الاستثناءات.
\n\nما هو التزامن المنظم (Structured Concurrency) ولماذا هو مطلوب؟
\nالتزامن المنظم (Structured Concurrency) هو مبدأ يتم فيه ربط دورة حياة الكوروتين بنطاق الرؤية (scope). إذا تم إلغاء الكوروتين الأصلي، يتم إلغاء جميع المهام الفرعية تلقائيًا. هذا يمنع تسرب الذاكرة ويجعل الكود قابلاً للتنبؤ.
\n\nfun main() = runBlocking {\n launch {\n delay(1000)\n println("المهمة 1 اكتملت")\n }\n launch {\n delay(500)\n println("المهمة 2 اكتملت")\n }\n println("الخيط الرئيسي لا يتم حظره")\n}\n\n\nالفرق بين launch و async
\nlaunch يبدأ كوروتينًا لا يُرجع نتيجة (Unit). async يُرجع Deferred، والذي يمكن الحصول على النتيجة منه عبر await(). async مناسب للحسابات المتوازية.
val deferred1 = async { calculateSum(10, 20) }\nval deferred2 = async { calculateSum(30, 40) }\nval result = deferred1.await() + deferred2.await()\nprintln("المجموع: $result")\n\n\nمعالجة الاستثناءات في الكوروتينات
\nاستخدم try-catch داخل الكوروتين أو CoroutineExceptionHandler للمعالجة الشاملة. من المهم تذكر أن الاستثناءات في launch تنتشر لأعلى في التسلسل الهرمي، بينما في async يتم تخزينها حتى استدعاء await().
val handler = CoroutineExceptionHandler { _, exception ->\n println("خطأ: ${exception.message}")\n}\n\nfun main() = runBlocking {\n val job = launch(handler) {\n throw RuntimeException("حدث خطأ ما")\n }\n}\n\n\n2. الدوال المضمنة (Inline functions)، reified، و crossinline
\nهذه الآليات تسمح بتحسين الأداء والعمل مع الأدوية (generics) في وقت التشغيل. غالبًا ما يسأل المحاورون عن الفرق بين inline و noinline و crossinline.
لماذا نحتاج الدوال المضمنة (inline functions)؟
\nالدوال المضمنة (Inline functions) تقوم بإدراج جسم الدالة مباشرة في مكان الاستدعاء. هذا يلغي التكاليف الإضافية لإنشاء كائنات lambda. مفيدة بشكل خاص للدوال ذات الترتيب الأعلى (higher-order functions).
\n\ninline fun measureTime(action: () -> Unit) {\n val start = System.currentTimeMillis()\n action()\n println("الوقت: ${System.currentTimeMillis() - start} مللي ثانية")\n}\n\n\nما هو reified؟
\nباستخدام reified يمكن الحصول على معلومات حول النوع داخل الدالة المضمنة (inline function)، وهو أمر مستحيل للأدوية (generics) العادية بسبب محو النوع (type erasure).
inline fun <reified T> isInstance(value: Any): Boolean {\n return value is T\n}\n\nfun main() {\n println(isInstance<String>("Hello")) // true\n}\n\n\nالفرق بين noinline و crossinline
\n- \n
noinline— يمنع تضمين (inlining) lambda معينة (على سبيل المثال، إذا تم تمريرها إلى دالة أخرى). \n crossinline— يسمح بالتضمين (inlining)، لكنه يمنع الإرجاع غير المحلي (non-local return) من lambda، وهو آمن للاستدعاءات غير المتزامنة. \n