Extension function في Kotlin: ما هي، بناء الجملة والتطبيق

المؤلف: IT Sectr نُشر: 2026-06-21 وقت القراءة: 9 دق

extension function — آلية في Kotlin تسمح بإضافة طرق جديدة إلى الفئات الموجودة دون وراثة ودون تعديل الكود المصدري. تُصرَّح الدالة ببادئة على شكل نوع المستقبل وتُستدعى كطريقة عادية لذلك النوع. وفقًا Kotlin Documentation, 2025، تُجمَّع extension functions كطرق ثابتة مع المستقبل كمعامل أول، مما يضمن صفر overhead في وقت التشغيل مقارنة بالاستدعاءات العادية.

النقاط الرئيسية

  • Extension function — طريقة بنوع مستقبل، تُستدعى كعضو في الفئة دون وراثة
  • نوع المستقبل يُحدد عبر نقطة قبل اسم الدالة: fun Type.method()
  • التوزيع الثابت — extension function غير افتراضية، تُستدعى حسب نوع المتغير
  • الاستيراد إلزامي: extension functions ليست أعضاء في الفئة وتتطلب استيرادًا
  • أعضاء الفئة لهم أولوية: إذا تطابقت الطريقة، يُستدعى عضو الفئة وليس extension

ما هي extension function في Kotlin؟

extension function هي دالة تُصرَّح خارج الفئة ولكن تُستدعى كطريقة تابعة لها. تأخذ نوع مستقبل يُضاف إليه الطريقة الجديدة. داخل الدالة، تشير الكلمة المفتاحية this إلى مثيل نوع المستقبل. يتيح هذا الأسلوب تمديد الفئات النهائية وفئات المكتبات الخارجية وحتى الأنواع البدائية دون إنشاء أغلفة.

على عكس Java، حيث تتطلب إضافة طريقة إلى فئة موجودة إما الوراثة أو فئة أدوات ثابتة، يقدم Kotlin حلاً أنيقًا دون boilerplate. وفقًا لدراسة Kotlin Foundation (2024)، تعد extension functions من بين أكثر خمس ميزات استخدامًا في اللغة — 78% من مطوري Kotlin يستخدمونها في عملهم اليومي.

يترجم مُجمِّع Kotlin extension function إلى طريقة ثابتة حيث يُمرَّر مثيل المستقبل كمعامل أول. هذا يعني أن extension functions لا تُعدِّل bytecode للفئة الهدف ولا تخترق التغليف — فقط الحقول والطرق العامة للمستقبل تكون قابلة للوصول.

استخدم extension functions لكتابة دوال أدوات نظيفة مرتبطة منطقيًا بنوع معين ولكن لا يمكن إضافتها إلى كوده المصدر.

بناء جملة extension function: التصريح والاستدعاء

بناء الجملة الأساسي لـ extension function: اسم نوع المستقبل، نقطة، اسم الدالة، ثم المعاملات ونوع الإرجاع. داخل الدالة، المستقبل قابل للوصول عبر this.

kotlin
// Extension function for String
fun String.isEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

// Called as regular String method
val result = "user@example.com".isEmail() // true

في هذا المثال، isEmail() يصبح متاحًا لجميع السلاسل النصية. داخل الدالة، this يشير إلى السلسلة النصية نفسها التي يُستدعى عليها الطريقة. يسمح Kotlin بحذف this في معظم الحالات — يمكنك كتابة contains("@") بدلاً من this.contains("@").

Extension function مع معاملات

يمكن أن تقبل extension functions معاملات إضافية وتُرجِع قيمًا من أي نوع. هذا يجعلها دوال كاملة، وليس مجرد سكر نحوي.

kotlin
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
    if (this.isEmpty()) return listOf(default)
    return this
}

val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)

المعامل default يُحدد القيمة المرجعة لقائمة فارغة. يمكن أن تكون extension function عامة — النوع List عام، على الرغم من أن المثال يستخدم List محددة.

التوزيع الثابت والمستقبل

تستخدم extension functions في Kotlin التوزيع الثابت، وليس التوزيع الافتراضي. هذا فرق رئيسي عن طرق الفئات العادية. أي extension function سيتم استدعاؤها يُحدد في وقت الترجمة حسب النوع الثابت للمتغير، وليس حسب نوعه الفعلي في وقت التشغيل.

kotlin
open class Animal
class Dog : Animal()

fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"

fun test() {
    val animal: Animal = Dog()
    println(animal.speak()) // "Animal sound" — static type Animal

    val dog: Dog = Dog()
    println(dog.speak()) // "Woof" — static type Dog
}

على الرغم من أن المتغير animal يشير إلى مثيل من Dog، إلا أنه يتم استدعاء extension function لـ Animal لأن النوع الثابت للمتغير هو Animal. لو كانت speak() طريقة افتراضية للفئة، لكان قد تم استدعاء Dog.speak(). هذا السلوك مهم في الاعتبار عند تصميم API باستخدام extension functions.

الاستيراد ونطاق extension functions

extension functions ليست أعضاء في الفئة — إنها دوال عادية تتطلب استيرادًا لاستخدامها في ملف آخر. يوفر Kotlin خيارين للاستيراد: باسم الدالة أو مع إعادة التسمية.

kotlin
// Import by name
import com.example.extensions.isEmail

// Import with alias (to resolve conflicts)
import com.example.extensions.isEmail as isValidEmail

// Call after import
val valid = "test@test.com".isEmail()

إعادة التسمية عبر as مفيدة في حالات تضارب الأسماء — على سبيل المثال، إذا كانت مكتبتان توفران extension function بنفس الاسم لنفس النوع. في هذه الحالة، يمكنك استيراد إحداهما باسم مختلف واستدعائها باستخدام الاسم الجديد.

النطاق

يمكن التصريح بـ extension functions على مستويات مختلفة:

المستوىالرؤيةمثال
Top-levelالمشروع بأكمله بعد الاستيرادfun String.isEmail()
Member extensionداخل الفئة المالكةclass A { fun B.ext() }
Localداخل دالةfun test() { fun String.ext() }

دوال member extension هي حالة خاصة حيث تُصرَّح extension function داخل فئة أخرى. في هذه الحالة، يكون كل من المستقبل (this الخاص بالدالة) وأعضاء الفئة الخارجية قابلين للوصول داخل extension function.

أولوية أعضاء الفئة على الامتدادات

إذا كانت الفئة تحتوي على طريقة بنفس التوقيع مثل extension function، فإن عضو الفئة يُستدعى دائمًا. لا تتجاوز extension function أبدًا طريقة الفئة — هذا قرار معماري لمنع التجاوزات العرضية.

kotlin
class User {
    fun greet() = "Hello from class"
}

fun User.greet() = "Hello from extension"

fun main() {
    val user = User()
    println(user.greet()) // "Hello from class"
}

حتى إذا تم تعريف extension function لاحقًا وبنفس التوقيع، سيختار المُجمِّع طريقة الفئة. يُصدر مُجمِّع Kotlin تحذيرًا عند اكتشاف هذه الحالة. الطريقة الوحيدة لاستدعاء extension function عند وجود طريقة فئة هي استدعاؤها كدالة عادية: greet(user).

أمثلة عملية على extension functions

في تطوير Android، أصبحت extension functions أداة قياسية للعمل مع View وContext وfragments. مكتبة Android KTX مبنية على extension functions، وتوفر أغلفة مناسبة لـ API الخاص بـ Android.

kotlin
// Extension function for working with View
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

// Extension function for Context — toast
fun Context.toast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

// Extension function as DSL builder
fun String.colorFormat(color: String): String =
    "$color$this\u001b[0m"

extension function toast() تجعل الاستدعاءات المعتمدة على Context موجزة: بدلاً من كتابة Toast.makeText(context, message, length).show()، يكفي كتابة context.toast("نص"). هذا يقلل من boilerplate ويجعل الكود أكثر قابلية للقراءة.

الأسئلة الشائعة

هل يمكن تجاوز extension function في فئة فرعية؟

لا، تستخدم extension functions التوزيع الثابت. إذا أعلنت extension function لفئة أساسية ونفسها لفئة فرعية، أي منها سيتم استدعاؤها يُحدد حسب النوع الثابت للمتغير في وقت الترجمة، وليس حسب النوع الفعلي في وقت التشغيل.

كيف تعمل extension functions مع الأنواع القابلة للصفر؟

يمكنك التصريح بـ extension functions لأنواع مستقبل قابلة للصفر: fun String?.isNullOrEmail(). داخل هذه الدالة، قد تكون this بقيمة null، لذلك يجب استخدام عوامل الاستدعاء الآمن أو الفحوصات الصريحة للصفر.

هل لدى extension functions overhead في الأداء؟

لا، تُجمَّع extension functions كطرق ثابتة. على مستوى bytecode، استدعاء extension function مطابق لاستدعاء طريقة ثابتة مع المستقبل كمعامل أول. لا يوجد انعكاس أو توزيع ديناميكي.

هل يمكن التصريح بـ extension property لفئة؟

نعم، تعمل extension properties بشكل مشابه لـ extension functions ولكن لا يمكنها تخزين حالة — فقط getter و setter. على سبيل المثال: val List.sum get() = reduce(Int::plus). كما تُجمَّع كطرق getter/setter ثابتة.

كيف تختلف extension functions عن الطرق في فئات الأدوات في Java؟

في Java تكتب StringUtils.isEmail(str)، بينما في Kotlin تكتب str.isEmail(). الفرق ليس فقط في بناء الجملة: extension functions تدعم الإكمال التلقائي في IDE، وتحسن قابلية قراءة سلاسل الاستدعاءات، وتسمح لـ IDE باقتراح دوال ذات صلة لنوع معين. Java لا تمتلك هذه الإمكانيات.

الملخص

  • Extension function — طريقة بنوع مستقبل، تُستدعى كعضو في الفئة دون وراثة
  • التوزيع الثابت — اختيار الدالة حسب النوع الثابت، وليس حسب نوع وقت التشغيل
  • صفر overhead — تُجمَّع كطريقة ثابتة مع المستقبل كمعامل أول
  • الاستيراد إلزامي للاستخدام في ملفات أخرى، الاسم المستعار عبر as مدعوم
  • أعضاء الفئة لهم أولوية على extension functions بنفس التوقيع
  • Member extensions — امتدادات مصرَّح بها داخل فئة ولها حق الوصول إلى أعضائها
  • الاستخدام العملي — أدوات لـ String و View و Context والمجموعات وبناء DSL

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا