الفرق بين المراجعتين لصفحة: «Kotlin/operator overloading»
أنشأ الصفحة ب'<noinclude>{{DISPLAYTITLE:التحميل الزائد للمعاملات (Operator Overloading) في لغة Kotlin}}</noinclude> تُتيح لغة Kotlin إجراءَ م...' |
ط استبدال النص - 'Kotlin Functions' ب'Kotlin Function' |
||
(مراجعتان متوسطتان بواسطة مستخدمين اثنين آخرين غير معروضتين) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE:التحميل الزائد للمعاملات (Operator Overloading) في لغة Kotlin}}</noinclude> | <noinclude>{{DISPLAYTITLE:التحميل الزائد للمعاملات (Operator Overloading) في لغة Kotlin}}</noinclude> | ||
تُتيح لغة Kotlin إجراءَ مجموعةٍ مُعرَّفة مسبقًا من العمليات على أنواع البيانات المختلفة وذلك باستخدام رموزٍ ثابتةٍ تعتمدها لغة Kotlin مثل الرمز <code>+</code> أو الرمز <code>*</code> وتختلف فيما بينها بالأولويّة (precedence)، حيث توجد دالةٌ (إما [[Kotlin/functions|دالة من الصنف [member]]] أو [[Kotlin/extensions|دالة إضافيّة [extension]]]) باسمٍ ثابتٍ لكلّ معاملٍ مُعرَّف بحسب النوع (نوعٌ يساريٌّ للعمليات الثنائيّة [binary operations] ونوع | تُتيح لغة Kotlin إجراءَ مجموعةٍ مُعرَّفة مسبقًا من العمليات على أنواع البيانات المختلفة وذلك باستخدام رموزٍ ثابتةٍ تعتمدها لغة Kotlin مثل الرمز <code>+</code> أو الرمز <code>*</code> وتختلف فيما بينها بالأولويّة (precedence)، حيث توجد دالةٌ (إما [[Kotlin/functions|دالة من الصنف [member]]] أو [[Kotlin/extensions|دالة إضافيّة [extension]]]) باسمٍ ثابتٍ لكلّ معاملٍ مُعرَّف بحسب النوع (نوعٌ يساريٌّ للعمليات الثنائيّة [binary operations] ونوع وسائط [argument type] للعمليات الأحاديّة [unary operations])، ويجب تحديد الدوال التي تحتوي على تحميلٍ زائدٍ للمعاملات بالمُحدَّد <code>operator</code>، تناقش الصفحة الاصطلاحات (conventions) التي تنظِّم التحميل الزائد لمختلف المعاملات. | ||
== العمليات الأحادية (Unary Operations) == | == العمليات الأحادية (Unary Operations) == | ||
سطر 19: | سطر 19: | ||
|} | |} | ||
يقوم المُترجِم (compiler) بالخطوات الآتية عند إجراء إحدى العمليات السابقة ولتكن <code>a+</code> : | يقوم المُترجِم (compiler) بالخطوات الآتية عند إجراء إحدى العمليات السابقة ولتكن <code>a+</code> : | ||
* تحديد نوع | * تحديد نوع المتغيِّر <code>a</code> وليكن <code>T</code> | ||
* البحث عن الدالة <code>unaryPlus()</code> المسبوقة بالمُحدِّد <code>operator</code> والتي لا تحتوي أيّة | * البحث عن الدالة <code>unaryPlus()</code> المسبوقة بالمُحدِّد <code>operator</code> والتي لا تحتوي أيّة معاملات للمستقبِل <code>T</code> (إما دالة من الصنف [member] أو دالة إضافيّة [extension]) | ||
* إن لم تكن الدالة موجودةً أو كانت غامضةً فسينتُج خطأٌ بالترجمة (compilation error) | * إن لم تكن الدالة موجودةً أو كانت غامضةً فسينتُج خطأٌ بالترجمة (compilation error) | ||
* إذا وُجدت الدالة وكان نوعها المُعاد <code>R</code> فسيكون للتعبير <code>a+</code> النوع <code>R</code> أيضًا | * إذا وُجدت الدالة وكان نوعها المُعاد <code>R</code> فسيكون للتعبير <code>a+</code> النوع <code>R</code> أيضًا | ||
سطر 43: | سطر 43: | ||
|<code>a.dec()</code> | |<code>a.dec()</code> | ||
|} | |} | ||
إذ ستُسنَد القيمة التي تعيدها الدالة <code>inc()</code> أو <code>dec()</code> إلى المتغيِّر الذي يُلحَق به المعامل <code>++</code> أو <code>--</code> دون أن تغيّر الدالةُ الكائنَ الذي استُدعيت من خلاله. يقوم المترجم بالخطوات الآتية لتنفيذ العملية اللاحقة (postfix) ولتكن <code>++a</code> مثلًا: | |||
* تحديد نوع | * تحديد نوع المتغيِّر<code>a</code> وليكن النوع <code>T</code> | ||
* البحث عن الدالة <code>inc()</code> والمُعرَّفة بالمُحدِّد <code>operator</code> وتكون بدون | * البحث عن الدالة <code>inc()</code> والمُعرَّفة بالمُحدِّد <code>operator</code> وتكون بدون معاملات وقابلةً للتطبيق على النوع <code>T</code> | ||
* التحقُّق من أنّ النوع المُعاد من هذه الدالة هو نوعٌ فرعيٌّ للنوع <code>T</code> | * التحقُّق من أنّ النوع المُعاد من هذه الدالة هو نوعٌ فرعيٌّ للنوع <code>T</code> | ||
وينتُج عن حساب التعبير: | وينتُج عن حساب التعبير: | ||
* تخزين القيمة الابتدائيّة (initial value) | * تخزين القيمة الابتدائيّة (initial value) للمتغيِّر <code>a</code> في متغيِّر مؤقت <code>a0</code> | ||
* إسناد قيمة نتيجة الدالة <code>inc()</code> إلى | * إسناد قيمة نتيجة الدالة <code>inc()</code> إلى المتغيِّر السابق <code>a</code> | ||
* إعادة | * إعادة المتغيِّر <code>a0</code> كنتيجةٍ للتعبير | ||
وتماثل خطوات تنفيذ العملية <code>--a</code> الخطوات السابقة. | وتماثل خطوات تنفيذ العملية <code>--a</code> الخطوات السابقة. | ||
أمّا معاملات الزيادة والنقصان بصيغتها البادئة (prefix) بالشكل: <code>a++</code> و <code>a--</code> فهي تعمل بالطريقة السابقة ذاتها وتكون النتيجة: | أمّا معاملات الزيادة والنقصان بصيغتها البادئة (prefix) بالشكل: <code>a++</code> و <code>a--</code> فهي تعمل بالطريقة السابقة ذاتها وتكون النتيجة: | ||
* إسناد نتيجة الدالة <code>a.inc()</code> إلى | * إسناد نتيجة الدالة <code>a.inc()</code> إلى المتغيِّر <code>a</code> | ||
* إعادة القيمة الجديدة | * إعادة القيمة الجديدة للمتغيِّر <code>a</code> كنتيجةٍ للتعبير | ||
== العمليات الثنائيّة (Binary Operations) == | == العمليات الثنائيّة (Binary Operations) == | ||
سطر 106: | سطر 106: | ||
|<code>!b.contains(a)</code> | |<code>!b.contains(a)</code> | ||
|} | |} | ||
وتكون الإجراءات نفسها في المعاملين حيث يُعكَس ترتيب | وتكون الإجراءات نفسها في المعاملين حيث يُعكَس ترتيب الوسائط (arguments) فقط. | ||
=== معاملات الوصول المفهرس (Indexed Access Operators) === | === معاملات الوصول المفهرس (Indexed Access Operators) === | ||
سطر 131: | سطر 131: | ||
|<code>a.set(i_1, ..., i_n, b)</code> | |<code>a.set(i_1, ..., i_n, b)</code> | ||
|} | |} | ||
إذ تُترجَم الأقواس <code>[]</code> إلى استدعاء الدالة <code>get</code> أو <code>set</code> بما يتناسب مع عدد | إذ تُترجَم الأقواس <code>[]</code> إلى استدعاء الدالة <code>get</code> أو <code>set</code> بما يتناسب مع عدد الوسائط. | ||
=== معاملات الاستدعاء (Invoke Operators) === | === معاملات الاستدعاء (Invoke Operators) === | ||
سطر 150: | سطر 150: | ||
|<code>a.invoke(i_1, ..., i_n)</code> | |<code>a.invoke(i_1, ..., i_n)</code> | ||
|} | |} | ||
إذ تُترجَم الأقواس <code>()</code> إلى استدعاءات للدالة <code>invoke</code> بالعدد المناسب من | إذ تُترجَم الأقواس <code>()</code> إلى استدعاءات للدالة <code>invoke</code> بالعدد المناسب من الوسائط. | ||
=== عمليات الإسناد المُحسَّنة (Augmented Assignments) === | === عمليات الإسناد المُحسَّنة (Augmented Assignments) === | ||
سطر 193: | سطر 193: | ||
ويكون معاملا التحقُّق من المرجعيّة (<code>===</code> و <code>==!</code>) غير قابلين للتحميل الزائد (not overloadable) وبالتالي ما من استخدامٍ اصطلاحيٍّ لهما. | ويكون معاملا التحقُّق من المرجعيّة (<code>===</code> و <code>==!</code>) غير قابلين للتحميل الزائد (not overloadable) وبالتالي ما من استخدامٍ اصطلاحيٍّ لهما. | ||
ويتميز المعامل <code>==</code> بأنه يُترجَم إلى تعبيرٍ معقَّدٍ يُضمِر القيمة <code>null</code> أي تكون نتيجة العملية <code>null == null</code> صحيحةً دائمًا، وتكون قيمة <code>x == null</code> خاطئةً دائمًا إن كان | ويتميز المعامل <code>==</code> بأنه يُترجَم إلى تعبيرٍ معقَّدٍ يُضمِر القيمة <code>null</code> أي تكون نتيجة العملية <code>null == null</code> صحيحةً دائمًا، وتكون قيمة <code>x == null</code> خاطئةً دائمًا إن كان المتغيِّر <code>x</code> متغيِّرًا nullable (أي لا يقبل القيمة الفارغة <code>null</code>) ولن تُستدعَى بذلك الدالة <code>x.equals()</code>. | ||
=== معاملات المقارنة (Comparison Operators) === | === معاملات المقارنة (Comparison Operators) === | ||
سطر 224: | سطر 224: | ||
[[تصنيف:Kotlin]] | [[تصنيف:Kotlin]] | ||
[[تصنيف:Kotlin Operators]] | [[تصنيف:Kotlin Operators]] | ||
[[تصنيف:Kotlin | [[تصنيف:Kotlin Function]] |
المراجعة الحالية بتاريخ 11:37، 30 أغسطس 2018
تُتيح لغة Kotlin إجراءَ مجموعةٍ مُعرَّفة مسبقًا من العمليات على أنواع البيانات المختلفة وذلك باستخدام رموزٍ ثابتةٍ تعتمدها لغة Kotlin مثل الرمز +
أو الرمز *
وتختلف فيما بينها بالأولويّة (precedence)، حيث توجد دالةٌ (إما دالة من الصنف [member] أو دالة إضافيّة [extension]) باسمٍ ثابتٍ لكلّ معاملٍ مُعرَّف بحسب النوع (نوعٌ يساريٌّ للعمليات الثنائيّة [binary operations] ونوع وسائط [argument type] للعمليات الأحاديّة [unary operations])، ويجب تحديد الدوال التي تحتوي على تحميلٍ زائدٍ للمعاملات بالمُحدَّد operator
، تناقش الصفحة الاصطلاحات (conventions) التي تنظِّم التحميل الزائد لمختلف المعاملات.
العمليات الأحادية (Unary Operations)
المعاملات الأحادية البادئة (Unary Prefix Operators)
التعبير | يُترجَم إلى |
---|---|
a+
|
a.unaryPlus()
|
a-
|
a.unaryMinus()
|
a!
|
a.not()
|
يقوم المُترجِم (compiler) بالخطوات الآتية عند إجراء إحدى العمليات السابقة ولتكن a+
:
- تحديد نوع المتغيِّر
a
وليكنT
- البحث عن الدالة
unaryPlus()
المسبوقة بالمُحدِّدoperator
والتي لا تحتوي أيّة معاملات للمستقبِلT
(إما دالة من الصنف [member] أو دالة إضافيّة [extension]) - إن لم تكن الدالة موجودةً أو كانت غامضةً فسينتُج خطأٌ بالترجمة (compilation error)
- إذا وُجدت الدالة وكان نوعها المُعاد
R
فسيكون للتعبيرa+
النوعR
أيضًا
وتُتاح هذه العمليات (بالإضافة لعملياتٍ أخرى) للأنواع الأساسيّة في لغة Kotlin ولا تتطلَّب استدعاء الدوال، فيمكن مثلًا استخدام التحميل الزائد للمعامل الأحاديّ -
بالشكل:
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
val point = Point(10, 20)
println(-point) // "(-10, -20)" سيظهر
معاملات الزيادة والنقصان
التعبير | يُترجَم إلى |
---|---|
++a
|
a.inc()
|
--a
|
a.dec()
|
إذ ستُسنَد القيمة التي تعيدها الدالة inc()
أو dec()
إلى المتغيِّر الذي يُلحَق به المعامل ++
أو --
دون أن تغيّر الدالةُ الكائنَ الذي استُدعيت من خلاله. يقوم المترجم بالخطوات الآتية لتنفيذ العملية اللاحقة (postfix) ولتكن ++a
مثلًا:
- تحديد نوع المتغيِّر
a
وليكن النوعT
- البحث عن الدالة
inc()
والمُعرَّفة بالمُحدِّدoperator
وتكون بدون معاملات وقابلةً للتطبيق على النوعT
- التحقُّق من أنّ النوع المُعاد من هذه الدالة هو نوعٌ فرعيٌّ للنوع
T
وينتُج عن حساب التعبير:
- تخزين القيمة الابتدائيّة (initial value) للمتغيِّر
a
في متغيِّر مؤقتa0
- إسناد قيمة نتيجة الدالة
inc()
إلى المتغيِّر السابقa
- إعادة المتغيِّر
a0
كنتيجةٍ للتعبير
وتماثل خطوات تنفيذ العملية --a
الخطوات السابقة.
أمّا معاملات الزيادة والنقصان بصيغتها البادئة (prefix) بالشكل: a++
و a--
فهي تعمل بالطريقة السابقة ذاتها وتكون النتيجة:
- إسناد نتيجة الدالة
a.inc()
إلى المتغيِّرa
- إعادة القيمة الجديدة للمتغيِّر
a
كنتيجةٍ للتعبير
العمليات الثنائيّة (Binary Operations)
المعاملات الحسابيّة (Arithmetic Operators)
التعبير | يُترجم إلى |
---|---|
a + b
|
a.plus(b)
|
a - b
|
a.minus(b)
|
a * b
|
a.times(b)
|
a / b
|
a.div(b)
|
a % b
|
a.rem(b) و a.mod(b) (مُستبعدة)
|
a..b
|
a.rangeTo(b)
|
تدعم Kotlin المعامل rem
بدءًا من الإصدار Kotlin 1.1 ليحل محلّ mod
في الإصدار Kotlin 1.0 (مُستبعد في الإصدار الحاليّ).
مثال
يبدأ الصنف Counter
الآتي بقيمةٍ ابتدائيّةٍ من الممكن زيادتها باستخدام التحميل الزائد للمعامل +
بالشكل:
data class Counter(val dayIndex: Int) {
operator fun plus(increment: Int): Counter {
return Counter(dayIndex + increment)
}
}
المعامل in
التعبير | يُترجَم إلى |
---|---|
a in b
|
b.contains(a)
|
a !in b
|
!b.contains(a)
|
وتكون الإجراءات نفسها في المعاملين حيث يُعكَس ترتيب الوسائط (arguments) فقط.
معاملات الوصول المفهرس (Indexed Access Operators)
التعبير | يُترجَم إلى |
---|---|
a[i]
|
a.get(i)
|
a[i,j]
|
a.get(i, j)
|
a[i_1, ..., i_n]
|
(a.get(i_1, ..., i_n
|
a[i] = b
|
a.set(i, b)
|
a[i, j] = b
|
a.set(i, j, b)
|
a[i_1, ..., i_n] = b
|
a.set(i_1, ..., i_n, b)
|
إذ تُترجَم الأقواس []
إلى استدعاء الدالة get
أو set
بما يتناسب مع عدد الوسائط.
معاملات الاستدعاء (Invoke Operators)
التعبير | يُترجَم إلى |
---|---|
a()
|
a.invoke()
|
a(i)
|
a.invoke(i)
|
a(i,j)
|
a.invoke(i,j)
|
a(i_1, ..., i_n)
|
a.invoke(i_1, ..., i_n)
|
إذ تُترجَم الأقواس ()
إلى استدعاءات للدالة invoke
بالعدد المناسب من الوسائط.
عمليات الإسناد المُحسَّنة (Augmented Assignments)
التعبير | يُترجَم إلى |
---|---|
a += b
|
a.plusِAssign(b)
|
a -= b
|
a.minusAssign(b)
|
a *= b
|
a.timesAssign(b)
|
a /= b
|
a.divAssign(b)
|
a %= b
|
a.remAssign(b) و a.modAssign(b) (مُستبعدة)
|
إذ يقوم المُترجِم (compiler) بالخطوات الآتية لعمليات الإسناد، ولتكن العملية a += b
مثلًا:
- إن كانت الدالة متاحةً:
- إن كانت الدالة الثنائيّة الموافقة لها (مثل الدالة
plus()
بالنسبة للدالةplusAssign()
) متاحةً أيضًا فيجب الإعلام بخطأ الالتباس (ambiguity) - التأكّد من أنّ النوع المُعاد هو
Unit
والإعلام بالخطأ خلاف ذلك - توليد الشيفرة المناسبة للدالة
a.plusِAssign(b)
- إن كانت الدالة الثنائيّة الموافقة لها (مثل الدالة
- إن لم تكن الدالة متاحةً: يحاول توليد الشيفرة
a = a + b
وهذا يتضمَّن التحقُّق من النوع؛ أي أن يكون نوع التعبيرa + b
نوعًا فرعيًا (subtype) منa
ولا تُعدُّ عمليات الإسنادا تعابيرًا (expressions) في Kotlin.
معاملات المساواة (Equality) والاختلاف (Inequality)
التعبير | يُترجَم إلى |
---|---|
a == b
|
a?.equals(b) ?: (b === null)
|
a != b
|
!(a?.equals(b) ?: (b === null))
|
ويكون معاملا التحقُّق من المرجعيّة (===
و ==!
) غير قابلين للتحميل الزائد (not overloadable) وبالتالي ما من استخدامٍ اصطلاحيٍّ لهما.
ويتميز المعامل ==
بأنه يُترجَم إلى تعبيرٍ معقَّدٍ يُضمِر القيمة null
أي تكون نتيجة العملية null == null
صحيحةً دائمًا، وتكون قيمة x == null
خاطئةً دائمًا إن كان المتغيِّر x
متغيِّرًا nullable (أي لا يقبل القيمة الفارغة null
) ولن تُستدعَى بذلك الدالة x.equals()
.
معاملات المقارنة (Comparison Operators)
التعبير | يُترجم إلى |
---|---|
a > b
|
a.compareTo(b) > 0
|
a < b
|
a.compareTo(b) < 0
|
a >= b
|
a.compareTo(b) >= 0
|
a <= b
|
a.compareTo(b) <= 0
|
إذ تُترجَم كافّة عمليات المقارنة إلى استدعاءاتٍ للدالة compareTo
التي تعيد النوع Int
.
معاملات تعميم الخاصّيّات (Property Delegation Operators)
وهي الدوال provideDelegate
و getValue
و setValue
ولمزيدٍ من المعلومات راجع الخاصّيّات المُعمَّمة (delegated properties).
استدعاء الدوال المسماة بالصيغة الضمنيّة (Infix)
تمكن محاكاة العمليّات بالصيغة الضمنيّة (infix) بالاعتماد على استدعاءات الدوال الضمنيّة.