أهم أخطاء مطوري Kotlin المبتدئين: من الألف إلى الياء

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

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

ابدأ الدورة

مقدمة: لماذا حتى المطورون الأذكياء يتعثرون في Kotlin

\n\n

Kotlin هي لغة أنيقة وقوية فازت بقلوب الملايين من المطورين. لكن إيجازها ومرونتها يخفيان فخاخًا، خاصة للمبتدئين القادمين من Java أو لغات أخرى. يعتقد الكثيرون أنه بما أن Kotlin "أوضح" من Java، فستكون الأخطاء أقل. في الواقع، العكس هو الصحيح: عدم فهم فلسفة اللغة يؤدي إلى أخطاء يصعب اكتشافها.

\n\n

في هذه المقالة، سنحلل أهم أخطاء مطوري Kotlin المبتدئين - من تجاهل null safety البسيط إلى الاستخدام الخاطئ للـ coroutines. سيتم توضيح كل خطأ بمثال كود ونصيحة عملية لتجنبه.

\n\n

1. Null Safety: متى ينقذ "?" ومتى لا

\n\n

1.1. تجاهل الأنواع القابلة للصفر (nullable types)

\n

الخطأ الأكثر شيوعًا هو استخدام !! (تأكيد عدم الصفر) حيث يمكن استخدام استدعاءات آمنة. غالبًا ما يكتب المبتدئون:

\n\n
fun printLength(str: String?) {\n    println(str!!.length) // سيؤدي إلى NPE إذا كان str == null\n}
\n\n

النهج الصحيح هو استخدام ?.let أو عامل الإلفيس ?::

\n\n
fun printLength(str: String?) {\n    str?.let { println(it.length) } ?: println("السلسلة فارغة")\n}
\n\n

1.2. نسيان التحويل الذكي (Smart Cast)

\n

يقوم Kotlin تلقائيًا بتحويل الأنواع بعد التحقق من الصفر، لكن المبتدئين لا يلاحظون ذلك:

\n\n
fun process(value: Any?) {\n    if (value is String) {\n        // value أصبح String بالفعل، لا حاجة للتحويل!\n        println(value.length) // يعمل بدون (value as String).length\n    }\n}
\n\n

نصيحة: ثق بالمترجم. استخدم !! فقط إذا كنت متأكدًا 100% أن القيمة ليست null، ومستعد للانهيار عند الخطأ.

\n\n

2. Sealed Class و When: حفر الشمولية

\n\n

2.1. استخدام فرع else مع sealed class

\n

تم إنشاء sealed class ليتحقق المترجم من معالجة جميع الخيارات. لكن المبتدئين غالبًا ما يضيفون else، مما يقتل السحر:

\n\n
sealed class Result {\n    data class Success(val data: String) : Result()\n    data class Error(val message: String) : Result()\n}\n\nfun handleResult(result: Result) {\n    when (result) {\n        is Result.Success -> println(result.data)\n        is Result.Error -> println(result.message)\n        else -> {} // خطأ! إذا أضفت فئة فرعية جديدة، لن يحذرك المترجم\n    }\n}
\n\n

الصحيح هو عدم استخدام else:

\n\n
fun handleResult(result: Result) {\n    when (result) {\n        is Result.Success -> println(result.data)\n        is Result.Error -> println(result.message)\n    } // سيعطي المترجم خطأ إذا لم يتم تغطية جميع الفروع\n}
\n\n

2.2. الخلط بين sealed class و enum

\n

غالبًا ما يختار المبتدئون enum حيث يحتاجون sealed class. تذكر: enum لمجموعة محدودة من الثوابت، sealed class لتسلسل هرمي ببيانات مختلفة في كل فئة فرعية.

\n\n

3. Scope Functions: apply, let, run, with, also — كيف لا تحتار

\n\n

3.1. خلط السياق: this مقابل it

\n

أكثر الالتباس شيوعًا هو عندما يستخدم المبتدئ apply (السياق — this) لعمليات تحتاج let (السياق — it):

\n\n
data class User(var name: String, var age: Int)\n\nfun initUser(): User {\n    return User("مجهول", 0).apply {\n        // this هو User\n        name = "إيفان"\n        age = 25\n    }\n}\n\nfun printUserInfo(user: User?) {\n    user?.let { \n        // it هو user\n        println("الاسم: ${it.name}, العمر: ${it.age}")\n    }\n}
\n\n

3.2. سلاسل scope functions بدون معنى

\n

يحب المبتدئون كتابة سلاسل مثل:

\n\n
list?.let { it.filter { it > 5 }.also { println(it) } }

                
                

المدونات

توصيات الكتب