State<List<User>?> ليس مفوضًا: أفضل 3 أخطاء وحلولها في Kotlin

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

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

ابدأ الدورة

ذعر صامت: عندما يصرخ Kotlin بأن State لا يمكن أن يكون مفوضًا

\n\n

أنت تكتب كودًا بلغة Kotlin، وتستخدم Jetpack Compose، وكل شيء يسير وفقًا للخطة. وفجأة - بام! - يصدر المترجم رسالة تجعل المبتدئ يشعر ببرودة في بطنه: Type 'State<List<User>?>' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate. تبدو وكأنها حكم بالإعدام، لكنها في الواقع مجرد صرخة استغاثة من الكود الخاص بك. دعنا نفهم ما يحاول قوله وكيف نتصالح معه.

\n\n

هذا الخطأ هو واحد من أشهر الأخطاء على Stack Overflow (آلاف المشاهدات!)، وهو يطارد المطورين الذين بدأوا للتو في تعلم الخصائص المفوضة في Kotlin. في هذه المقالة، لن نترجم رسالة الخطأ فحسب، بل سنعرض ثلاثة سيناريوهات حقيقية يظهر فيها الخطأ، ونقدم حلولًا عملية. ستتعلم كيفية استخدام StateFlow في Compose بشكل صحيح، وكيف لا تتشوش في مفوضات Kotlin، ولماذا يتصرف LiveData أحيانًا بشكل غير متوقع.

\n\n

ما هو المفوض في Kotlin ولماذا لا يصبح State واحدًا؟

\n\n

بداية - شرح سريع. الخصائص المفوضة في Kotlin (الكلمة المفتاحية by) تسمح بنقل منطق getter و setter إلى كائن منفصل. على سبيل المثال:

\n\n
var name: String by Delegates.observable("") { prop, old, new ->\n    println("$old → $new")\n}\n
\n\n

لكي يتمكن الكائن من أن يكون مفوضًا، يجب أن ينفذ الطريقتين getValue() وربما setValue() بتوقيع معين. الخطأ Type 'State<List<User>?>' has no method 'getValue(Nothing?, KProperty<*>)' يشير تحديدًا إلى هذا: الكائن الخاص بك من النوع State<List<User>?> لا يحتوي على طريقة getValue مناسبة. لا يمكن للمترجم استخدامه كمفوض.

\n\n

الآن ننتقل إلى ثلاثة سيناريوهات محددة يظهر فيها هذا الخطأ في أغلب الأحيان.

\n\n

الخطأ 1: الخلط بين by وتعيين StateFlow

\n\n

الحالة الأكثر شيوعًا - عندما يحاول المطور استخدام by لتعيين StateFlow في ViewModel، لكنه يفعل ذلك بشكل خاطئ. انظر إلى هذا الكود:

\n\n
// ❌ خطأ! لا يمكن فعل ذلك\nclass MyViewModel : ViewModel() {\n    private val _users = MutableStateFlow<List<User>?>(null)\n    val users: StateFlow<List<User>?> by _users  // خطأ!\n}\n
\n\n

هنا _users هو MutableStateFlow، ونحن نحاول جعله مفوضًا للخاصية users. لكن StateFlow لا ينفذ getValue لخصائص Kotlin. إنه ليس مفوضًا، بل تدفق تفاعلي.

\n\n

الحل: استخدم asStateFlow()

\n\n

الطريقة الصحيحة - ليست التفويض، بل التحويل:

\n\n
// ✅ صحيح\nclass MyViewModel : ViewModel() {\n    private val _users = MutableStateFlow<List<User>?>(null)\n    val users: StateFlow<List<User>?> = _users.asStateFlow()\n}\n
\n\n

الطريقة asStateFlow() تنشئ نسخة غير قابلة للتغيير يمكن عرضها بأمان للخارج. لا مفوضات، لا أخطاء.

\n\n

الخطأ 2: استخدام by مع LiveData في Compose

\n\n

ثاني أشهر حالة - محاولة استخدام LiveData مباشرة مع by في Jetpack Compose. المطورون القادمون من عالم أنظمة View اعتادوا على LiveData ويحاولون تطبيق نفس النهج:

\n\n
// ❌ خطأ!\n@Composable\nfun UserList(viewModel: MyViewModel) {\n    val users: List<User>? by viewModel.users  // خطأ!\n    // ...\n}\n
\n\n

هنا viewModel.users من النوع LiveData<List<User>?>. و LiveData ليس مفوضًا لخصائص Compose. المترجم يعطي نفس الخطأ مرة أخرى: Type 'LiveData<List<User>?>' has

المدونات

توصيات الكتب