Kotlin / أسس اللغة والحالة الصريحة / الدرس 3
تمثيل الغياب بأنواع تقبل null
استخدم التحويلات الذكية والاستدعاءات الآمنة وتعبيرات Elvis والتحليل القابل للخلو، دون استبدال معنى المجال بتوكيدات غير متحققة.
في هذه الصفحة
أهداف الدرس
- ميّز القيمة الغائبة من النص الفارغ ومن القيمة الخارجية غير الصالحة.
- اختر بديلا صريحا أو عودة مبكرة بدلا من توكيد عدم الخلو عندما لا توجد ضرورة مبررة له.
قابلية الخلو جزء من النوع المعلن
يعد النوع String بنص غير خال وفق قواعد أنواع Kotlin، بينما يسمح String? بنص أو بالقيمة null. يختلف الغياب عن النص الفارغ؛ فالنص الفارغ قيمة موجودة طولها صفر. اختر التمثيل وفقا للمجال، مثل لقب اختياري أو بحث لم يعثر على نتيجة. يكشف نظام أمان الخلو أخطاء كثيرة وقت الترجمة، لكنه لا يضمن استحالة كل مشكلة خلو أثناء التشغيل، خاصة عند التكامل مع Java أو خلال تهيئة الكائن. لذلك لا تصف Kotlin بأنها تجعل جميع الإخفاقات المتعلقة بـnull مستحيلة.
يحفظ الاستدعاء الآمن احتمال الغياب
يستدعي العامل ?. العضو فقط عندما لا يكون المستقبل null، وينتج null بخلاف ذلك. يمكن أن تعبّر سلسلة استدعاءات آمنة عن مسار اختياري، لكن طول السلسلة قد يخفي أي قيمة غائبة هي المهمة. يقدّم Elvis بديلا عندما يكون التعبير على يساره null فقط، وليس عندما يكون النص فارغا أو العدد صفرا. يوضح المثال هذه الفروق بقيم ثابتة. المخرجات المتوقعة مستنتجة من دلالات اللغة؛ ولم يُجر في هذه البيئة اختبار بالمترجم أو اختبار خاص بمنصة تشغيل.
fun main() {
val missing: String? = null
val empty: String? = ""
println(missing?.length ?: -1)
println(empty?.length ?: -1)
println(missing ?: "anonymous")
}
-1
0
anonymous
المخرجات المتوقعة هي -1 ثم 0 ثم anonymous. النص الفارغ ليس null، ولذلك يعطي طولا صفريا ولا يختار Elvis البديل -1. لم يُنفّذ البرنامج فعليا.
دع الدليل المستقر يضيّق النوع
بعد فحص null مناسب، يستطيع Kotlin إجراء تحويل ذكي لقيمة مستقرة إلى نوع غير خال. والاستقرار مهم: فقد لا تعيد خاصية قابلة للتعديل، أو ذات getter مخصص، أو قيمة تتغير خارجيا النتيجة نفسها بين الفحص والوصول. احفظ القيمة في متغير محلي val عندما تكون اللقطة هي المعنى المقصود. لا تُسكت تعذّر التحويل الذكي باستخدام !! لمجرد إرضاء المترجم؛ بل افحص هل يمكن أن يتغير المصدر فعلا. يجب أن يتعلق الفحص الناجح والوصول اللاحق بالقيمة نفسها وفق افتراضات الشيفرة.
افصل فشل التحليل المتوقع عن صحة المجال
تعيد toIntOrNull القيمة null عندما لا يمكن تمثيل النص بوصفه Int، سواء بسبب صياغة غير صالحة أو قيمة خارج المجال العددي. وقد يُحلّل عدد سالب بنجاح لكنه يخالف قاعدة عدد المقاعد؛ فالتحليل والتحقق الدلالي خطوتان مختلفتان. تستخدم الدالة عودة مبكرة للصياغة غير الصالحة وفحص نطاق صريحا للمعنى. يصلح إرجاع null للسببين فقط عندما لا يحتاج المستدعي إلى تمييزهما. ويمكن لنمذجة النتائج المغلقة لاحقا حفظ أسباب مفصلة، دون استعمال الاستثناءات للتحكم في مسار إخفاق عادي متوقع.
fun positiveSeats(text: String): Int? {
val value = text.toIntOrNull() ?: return null
return if (value > 0) value else null
}
fun main() {
for (text in listOf("3", "bad", "0")) {
println(positiveSeats(text) ?: "invalid")
}
}
3
invalid
invalid
المخرجات المتوقعة هي 3 ثم invalid ثم invalid. يُحلَّل الصفر بنجاح، لكنه يفشل في قاعدة وجوب العدد الموجب. جميع النصوص مدخلات تعليمية ثابتة؛ ولا يقرأ البرنامج مدخلا تفاعليا.
استخدم !! فقط عند وجود قيد داخلي مبرر
يحوّل !! التعبير القابل للخلو إلى توقع بعدم الخلو، ويرمي استثناء إذا كانت القيمة null. فهو ينقل المسؤولية إلى المبرمج، ولا يتحقق من البيانات الخارجية بطريقة آمنة. فضّل رفضا صريحا أو بديلا أو عودة مبكرة عندما يكون الغياب أمرا عاديا. وعند حد التكامل، حدّد هل تعني البيانات المفقودة عدم العثور أو عدم الإتاحة أو التشوه أو إساءة برمجية. قد يخفي إسناد البديل نفسه للجميع عيبا خطيرا، بينما يجعل الرمي عند غياب اعتيادي مسار عمل متوقعا أكثر هشاشة بلا حاجة.
الأخطاء الشائعة وإصلاحها
يفشل توكيد عدم الخلو عندما يغيب المدخل.
- لماذا يحدث؟
- عومل الغياب المتوقع كما لو كان حالة داخلية مستحيلة.
- كيف تصلحه؟
- عالج null صراحة عند حد استقبال القيمة.
- كيف تتجنب تكراره؟
- احصر التوكيدات في القيود الداخلية التي توجد مبررات حقيقية لصحتها.
لا يختار اللقب الفارغ النص البديل.
- لماذا يحدث؟
- يفحص Elvis القيمة null، ولا يفحص كون النص فارغا أو مكوّنا من مسافات.
- كيف تصلحه؟
- طبّق قاعدة صريحة للفراغ إذا كان المجال يتطلب ذلك.
- كيف تتجنب تكراره؟
- اختبر null والنص الفارغ والمسافات والنص العادي كلًّا على حدة.
يُقبل صفر محلّل بوصفه عدد مقاعد موجبا.
- لماذا يحدث؟
- اختلط تحويل الصياغة بالتحقق من قاعدة العمل.
- كيف تصلحه؟
- افحص النطاق المسموح بعد نجاح التحليل.
- كيف تتجنب تكراره؟
- أبق التحليل وقيود المجال خطوتين مميزتين ظاهرتين للقارئ.
اختبر فهمك
1. لماذا يكون value?.length ?: 0 ملتبسا في تقرير يجب أن يميّز النص الغائب من النص الفارغ؟
اعرض الحل المشروح
ينتج التعبير صفرا لكل من null والنص الفارغ، فيفقد التقرير التمييز الذي يحتاجه. احتفظ بطول قابل للخلو أو حالة مسماة أو تمثيل صريح آخر، حتى تقرر طبقة العرض كيفية إظهار كل حالة. لا يكون البديل المريح صحيحا إلا عندما يكون دمج المعنيين مقصودا.
2. ما مشكلة text.toIntOrNull()!! عند استقبال عدد مقاعد من المستخدم؟
اعرض الحل المشروح
يتحول النص المشوّه أو الخارج عن مجال Int إلى فشل في توكيد عدم الخلو، بينما تظل الأعداد السالبة الصحيحة نحويا مجتازة لمرحلة التحليل. أعد نتيجة صريحة للمدخل غير الصالح، ثم افرض النطاق الموجب. وإذا احتاج المستدعي إلى أسباب مختلفة للرفض، فاستخدم نتيجة منظمة تحفظها بدلا من حذف السبب.
أهم ما تعلمته
- تجعل الأنواع القابلة للخلو الغياب ظاهرا، لكن المجال يحدد معناه.
- لا تحل الاستدعاءات الآمنة وElvis محل التحقق من البيانات أو تحديد ملكيتها.