آلية expect/actual لواجهات برمجة المنصات
صرّح عن التعريفات المتوقعة في الشيفرة المشتركة وقدّم تطبيقات فعلية لكل منصة.
آلية expect/actual لواجهات برمجة المنصات درس مجاني في Kotlin Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Kotlin Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
نمط expect/actual
يعلن expect عن عقد API في commonMain. وتوفر كل منصة تطبيقًا باستخدام actual. ويفرض المترجم أن يتوفر actual لجميع الأهداف.
// commonMain:
expect fun platformName(): String
// androidMain:
actual fun platformName(): String = "Android ${android.os.Build.VERSION.SDK_INT}"
// iosMain:
actual fun platformName(): String = UIDevice.currentDevice.systemName()فئة expect
يعلن expect class عن فئة وواجهة API الخاصة بها في commonMain. وتوفر كل منصة التطبيق الكامل لها.
// commonMain:
expect class DateTime {
fun format(pattern: String): String
val timestamp: Long
}
// androidMain:
actual class DateTime {
actual val timestamp = System.currentTimeMillis()
actual fun format(pattern: String): String =
java.text.SimpleDateFormat(pattern).format(java.util.Date(timestamp))
}كائن expect
يعلن expect object عن singleton مع عقده. ويفيد ذلك في الأدوات الخاصة بالمنصة، مثل التسجيل والإعدادات والتحليلات.
// commonMain:
expect object Logger {
fun debug(msg: String)
fun error(msg: String, cause: Throwable? = null)
}
// androidMain:
actual object Logger {
actual fun debug(msg: String) = android.util.Log.d("App", msg)
actual fun error(msg: String, cause: Throwable?) =
android.util.Log.e("App", msg, cause)
}expect val / fun على المستوى الأعلى
تُعد expect val وexpect fun على المستوى الأعلى بسيطة، وتناسبان الأدوات الخاصة بالمنصة التي لا تحتفظ بحالة.
// commonMain:
expect val isDebugBuild: Boolean
expect fun generateUUID(): String
// androidMain:
actual val isDebugBuild: Boolean = BuildConfig.DEBUG
actual fun generateUUID(): String = java.util.UUID.randomUUID().toString()
// iosMain:
actual val isDebugBuild: Boolean = false // or a native flag
actual fun generateUUID(): String = NSUUID().UUIDString()expect fun مع تطبيق افتراضي
بدءًا من Kotlin 1.9+، يمكن أن يتضمن expect fun تطبيقًا افتراضيًا باستخدام expect fun f() { }. ويمكن للمنصات استبدال التطبيق الافتراضي أو استخدامه.
// commonMain (Kotlin 1.9+):
expect fun log(msg: String) {
println("[DEFAULT] $msg") // used if no actual overrides
}
// androidMain:
actual fun log(msg: String) = android.util.Log.d("KMP", msg)
// iosMain (uses default):
// no actual needed — default is usedtypealias فعلي
إذا كانت المنصة تحتوي مسبقًا على الفئة المطابقة تمامًا لما تحتاج إليه، فاستخدم actual typealias للإشارة إليها بدلًا من تغليفها.
// commonMain:
expect class AtomicInt {
fun get(): Int
fun incrementAndGet(): Int
}
// androidMain / jvmMain:
actual typealias AtomicInt = java.util.concurrent.atomic.AtomicInteger
// iosMain:
actual class AtomicInt {
private var value = 0
actual fun get() = value
actual fun incrementAndGet() = ++value
}واجهة expect (نمط غير مباشر)
لمزيد من المرونة، أعلن عن واجهة في commonMain، واستخدم expect/actual فقط للمصنع الذي ينشئ نسخة المنصة.
// commonMain:
interface FileStorage {
suspend fun readFile(path: String): ByteArray
suspend fun writeFile(path: String, data: ByteArray)
}
expect fun createFileStorage(): FileStorage
// androidMain:
actual fun createFileStorage(): FileStorage = AndroidFileStorage()
// iosMain:
actual fun createFileStorage(): FileStorage = IosFileStorage()الاختبار باستخدام expect/actual
اختبر API المشتركة في commonTest. وتعمل التطبيقات الفعلية أثناء مهام الاختبار الخاصة بالمنصة، مثل testDebugUnitTest على Android وiosX64Test على iOS.
// commonTest:
import kotlin.test.Test
import kotlin.test.assertNotNull
class PlatformTest {
@Test
fun testUUID() {
val uuid = generateUUID()
assertNotNull(uuid)
println("UUID: $uuid")
}
}أخطاء شائعة
يجب أن يكون لكل تصريح expect تصريح actual في جميع الأهداف المُعدّة، وإلا يفشل البناء. ويُكتشف غياب actual أثناء الترجمة.
// If you add a new target (e.g., jvmMain), you must add:
// actual fun platformName(): String = "JVM"
// Otherwise:
// error: Expected function platformName has no actual declaration in module :shared for JVM
fun main() { println("Missing actuals = compile error") }فئة التعليقات التوضيحية expect
تتيح expect annotation class ربط التعليقات التوضيحية الخاصة بالمنصة، مثل ربط تعليق توضيحي مشترك شبيه بـ@Parcelize بـAndroid Parcelize plugin.
// commonMain:
expect annotation class CommonParcelize()
// androidMain:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
actual annotation class CommonParcelize
// iosMain:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
actual annotation class CommonParcelizeتطبيق عملي: Keychain/Preferences
من الأمثلة العملية المعتادة على expect/actual واجهة لتخزين آمن، حيث يستخدم Android EncryptedSharedPreferences، بينما يستخدم iOS Keychain.
// commonMain:
expect class SecureStorage {
fun save(key: String, value: String)
fun get(key: String): String?
}
// androidMain:
actual class SecureStorage {
private val prefs = /* EncryptedSharedPreferences */ mapOf<String,String>()
actual fun save(key: String, value: String) { /* ... */ }
actual fun get(key: String): String? = null // simplified
}تحقق سريع
ماذا يحدث أثناء الترجمة إذا لم يكن لتصريح expect تصريح actual لأحد الأهداف؟
مراجعة
يعلن expect عن API في commonMain، بينما يوفر actual تطبيق المنصة. استخدم actual typealias عندما تحتوي المنصة مسبقًا على النوع المطلوب. ويُكتشف غياب actual أثناء الترجمة، لذلك لا توجد مفاجآت وقت التشغيل.
الأسئلة الشائعة
هل درس «آلية expect/actual لواجهات برمجة المنصات» مجاني؟
نعم — نص درس «آلية expect/actual لواجهات برمجة المنصات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Kotlin Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
ماذا ستتعلم في «آلية expect/actual لواجهات برمجة المنصات»؟
صرّح عن التعريفات المتوقعة في الشيفرة المشتركة وقدّم تطبيقات فعلية لكل منصة. تتمرن على Kotlin Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Kotlin Academy؟
لا تُشترط خبرة سابقة. Kotlin Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «آلية expect/actual لواجهات برمجة المنصات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Kotlin Academy هذا؟
نعم. كل درس في Kotlin Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- بنية مشروع KMP: commonMain وandroidMain وiosMain
- آلية expect/actual لواجهات برمجة المنصات
- مشاركة طبقتَي Repository وUse Case
- حقن الاعتماديات في KMP باستخدام Koin