ما الذي يخفيه علامة الاستفهام؟ نغوص في nullable في Kotlin
\n\nهل سبق لك أن كتبت كودًا بلغة Java وأصبت بـ NullPointerException في أكثر الأوقات غير المناسبة؟ إذا كان الأمر كذلك - فأنت تعلم كم هو مؤلم. لقد حل مطورو Kotlin هذه المشكلة مرة واحدة وإلى الأبد، وجعلوا null safety ليس مجرد ميزة، بل فلسفة للغة. لكن المبتدئين غالبًا ما يخافون من السؤال: "In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them?" (هكذا بالضبط يبدو السؤال الأكثر شيوعًا على Stack Overflow).
اليوم سنشرح جميع اصطلاحات التعامل مع nullable: من ?. البسيط إلى let الذكي و !! السحري. والأهم من ذلك - سننظر تحت الغطاء لنفهم كيف يعمل هذا على مستوى البايت كود. هيا بنا!
نبذة تعريفية سريعة: ما هو nullable؟
\n\nفي Kotlin، تنقسم الأنواع إلى معسكرين: nullable (يمكن أن تكون null) و non-nullable (لا يمكن). إذا كتبت var name: String = null - سيصدر المترجم خطأ. أما var name: String? = null - فهو مقبول. علامة الاستفهام هي علامة تقول: "مرحبًا أيها المترجم، أعلم أنه قد يكون هناك قيمة فارغة هنا، فلا تغضب".
ولكن كيف نتعامل مع هذا؟ مجرد التحقق من null باستخدام if - هذا ممل وليس بطريقة Kotlin. دعنا نلقي نظرة على الأدوات الحقيقية.
الثلاثي المنقذ: ?. و ?: و ?.
\n\nتقدم Kotlin ثلاثة أركان يقوم عليها كل التعامل مع nullable. احفظها - ستوفر لك ساعات من التصحيح.
\n\n1. عامل الاستدعاء الآمن (?.)
\n\nهذا هو الضيف الأكثر شيوعًا في كودك. يسمح لك عامل safe call باستدعاء دالة أو خاصية فقط إذا كان الكائن لا يساوي null. إذا كان الكائن null - سيعيد العامل قيمة null، ولن يحدث أي استثناء.
\n\nval user: User? = null\nval name: String? = user?.name // name = null, لا يوجد خطأ\n\nتحت الغطاء، يتحول هذا إلى بايت كود يقوم بفحص عادي للـ null واستدعاء شرطي. لا سحر، فقط أمان.
\n\n2. عامل Elvis (?:)
\n\nElvis هو صديقك عندما تحتاج إلى تعيين قيمة افتراضية. إذا كان الجزء الأيسر ليس null - نأخذه، وإلا - نأخذ ما على اليمين.
\n\nval name: String? = null\nval displayName: String = name ?: "ضيف" // "ضيف"\n\nيمكن استخدام Elvis أيضًا للخروج المبكر من الدالة:
\n\nfun getUserName(user: User?): String {\n val name = user?.name ?: return "مجهول"\n return name\n}\n\n3. تأكيد عدم الصفرية (!!)
\n\nهذا هو العامل المتمرد. إنه يقول: "أنا متأكد أنه ليس null هنا، ومستعد للموت (بمعنى، إلقاء NPE) إذا كنت مخطئًا". استخدمه بحذر، فقط عندما تكون متأكدًا 100%.
\n\nval user: User? = getUserFromDatabase() // نحن متأكدون من وجوده\nval name: String = user!!.name\n\nتحت الغطاء، يتم ترجمة !! إلى فحص عادي مع إلقاء NullPointerException عند وجود null. لا شيء معقد، ولكنه خطير.
الطريقة الاصطلاحية: let, run, apply ورفاقهم
\n\nالآن ننتقل إلى الجزء الأكثر إثارة للاهتمام. السؤال على Stack Overflow يدور حول هذا بالضبط: كيفية تحويل القيم nullable بشكل اصطلاحي. الإجابة هي دوال الإضافة من المكتبة القياسية.
\n\nlet - ملك تحويلات nullable
\n\nتقوم الدالة let بتنفيذ كتلة من الكود فقط إذا كان الكائن ليس null. داخل الكتلة، يكون الكائن متاحًا كـ it (أو يمكن إعادة تسميته).
val user: User? = getUser()\nuser?.let { \n println("اسم المستخدم: ${it.name}")\n // هنا it - مضمون أنه ليس null\n}\n\nهذه هي الطريقة الاصطلاحية لتنفيذ إجراء فقط عندما تكون القيمة غير null. بدون let، كان علينا كتابة:
if (user != null) {\n println("اسم المستخدم: ${user.name}")\n}