الفرق بين المراجعتين لصفحة: «Kotlin/operator overloading»
أنشأ الصفحة ب'<noinclude>{{DISPLAYTITLE:التحميل الزائد للمعاملات (Operator Overloading) في لغة Kotlin}}</noinclude> تُتيح لغة Kotlin إجراءَ م...' |
لا ملخص تعديل |
||
سطر 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>a</code> وليكن النوع <code>T</code> | ||
مراجعة 11:36، 19 مارس 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)
|
وتكون الإجراءات نفسها في المعاملين حيث يُعكَس ترتيب المتحولات فقط.
معاملات الوصول المفهرس (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) بالاعتماد على استدعاءات الدوال الضمنيّة.