هذا الدرس العملي حول الترميز هو جزء من دورة Kotlin التدريبية للمبرمجين. يمكنك تحقيق أقصى استفادة من هذه الدورة التدريبية إذا اتبعت ترتيب الخطوات في دروس البرمجة. استنادًا إلى معرفتك، قد تتمكّن من قراءة بعض الأقسام بسرعة. هذه الدورة التدريبية موجّهة للمبرمجين الذين يعرفون لغة برمجة تعتمد على العناصر ويريدون تعلُّم Kotlin.
مقدمة
في هذا الدرس العملي، ستتعرّف على الفئات والدوال والطرق العامة وكيفية عملها في Kotlin.
بدلاً من إنشاء تطبيق نموذجي واحد، تم تصميم الدروس في هذه الدورة التدريبية لتعزيز معرفتك، ولكنها شبه مستقلة عن بعضها البعض حتى تتمكن من تصفّح الأقسام التي تعرفها. ولربطها ببعضها، تستخدم العديد من الأمثلة سمة حوض السمك. إذا أردت الاطّلاع على قصة حوض السمك بالكامل، يمكنك الرجوع إلى دورة Kotlin Bootcamp للمبرمجين على Udacity.
ما يجب معرفته
- بنية دوال وفئات وطُرق Kotlin
- كيفية إنشاء صف جديد في IntelliJ IDEA وتشغيل برنامج
أهداف الدورة التعليمية
- كيفية استخدام الفئات والطُرق والدوال العامة
الإجراءات التي ستنفذّها
- إنشاء فئة عامة وإضافة قيود
- إنشاء النوعَين
inوout - إنشاء دوال وطُرق ودوال إضافية عامة
مقدمة حول الأنواع العامة
تحتوي لغة Kotlin، مثل العديد من لغات البرمجة، على أنواع عامة. يتيح لك النوع العام إنشاء فئة عامة، وبالتالي جعل الفئة أكثر مرونة.
لنفترض أنّك بصدد تنفيذ فئة MyList تحتوي على قائمة بعناصر. بدون الأنواع العامة، ستحتاج إلى تنفيذ إصدار جديد من MyList لكل نوع: إصدار لـ Double، وإصدار لـ String، وإصدار لـ Fish. باستخدام الأنواع العامة، يمكنك جعل القائمة عامة، وبالتالي يمكنها الاحتفاظ بأي نوع من العناصر. وهو يشبه جعل النوع حرف بدل يمكن أن يتناسب مع العديد من الأنواع.
لتحديد نوع عام، ضَع T بين قوسين زاويين <T> بعد اسم الفئة. (يمكنك استخدام حرف آخر أو اسم أطول، ولكنّ الاصطلاح الخاص بالنوع العام هو T).
class MyList<T> {
fun get(pos: Int): T {
TODO("implement")
}
fun addItem(item: T) {}
}يمكنك الإشارة إلى T كما لو كان نوعًا عاديًا. نوع القيمة المعروضة للدالة get() هو T، ونوع المَعلمة للدالة addItem() هو T. بالطبع، القوائم العامة مفيدة جدًا، لذا تم إنشاء الفئة List في Kotlin.
الخطوة 1: إنشاء تسلسل هرمي للأنواع
في هذه الخطوة، ستنشئ بعض الفئات لاستخدامها في الخطوة التالية. تم تناول موضوع الفئات الفرعية في درس تطبيقي سابق حول الترميز، ولكن إليك مراجعة موجزة.
- لإبقاء المثال بسيطًا، أنشئ حزمة جديدة ضمن src وسمِّها
generics. - في حزمة generics، أنشئ ملف
Aquarium.ktجديدًا. يتيح لك ذلك إعادة تحديد العناصر باستخدام الأسماء نفسها بدون حدوث تعارضات، لذا سيتم وضع بقية الرمز البرمجي لهذا الدرس العملي في هذا الملف. - أنشئ تسلسلاً هرميًا لأنواع إمدادات المياه. ابدأ بإنشاء
WaterSupplyفئةopen، حتى يمكن إنشاء فئات فرعية منها. - أضِف مَعلمة منطقية
var،needsProcessing. يؤدي ذلك تلقائيًا إلى إنشاء خاصية قابلة للتغيير، بالإضافة إلى دالة جلب ودالة ضبط. - أنشئ فئة فرعية
TapWaterتوسّعWaterSupply، وأدخِلtrueبدلاً منneedsProcessing، لأنّ مياه الصنبور تحتوي على مواد مضافة تضرّ بالأسماك. - في
TapWater، عرِّف دالة باسمaddChemicalCleaners()تضبط قيمةneedsProcessingعلىfalseبعد تنظيف المياه. يمكن ضبط السمةneedsProcessingمنTapWater، لأنّهاpublicتلقائيًا ويمكن الوصول إليها من الفئات الفرعية. في ما يلي الرمز البرمجي المكتمل.
package generics
open class WaterSupply(var needsProcessing: Boolean)
class TapWater : WaterSupply(true) {
fun addChemicalCleaners() {
needsProcessing = false
}
}- أنشئ فئتَين فرعيتَين إضافيتَين من
WaterSupply، اسميهماFishStoreWaterوLakeWater. لا تحتاج السمةFishStoreWaterإلى معالجة، ولكن يجب فلترة السمةLakeWaterباستخدام الطريقةfilter(). بعد الفلترة، لن تحتاج إلى معالجتها مرة أخرى، لذا فيfilter()، اضبطneedsProcessing = false.
class FishStoreWater : WaterSupply(false)
class LakeWater : WaterSupply(true) {
fun filter() {
needsProcessing = false
}
}إذا كنت بحاجة إلى معلومات إضافية، راجِع الدرس السابق حول الميراث في Kotlin.
الخطوة 2: إنشاء فئة عامة
في هذه الخطوة، ستعدّل الفئة Aquarium لتتوافق مع أنواع مختلفة من إمدادات المياه.
- في ملف Aquarium.kt، حدِّد فئة
Aquarium، مع وضع<T>بين قوسين بعد اسم الفئة. - أضِف سمة غير قابلة للتغيير
waterSupplyمن النوعTإلىAquarium.
class Aquarium<T>(val waterSupply: T)- اكتب دالة باسم
genericsExample(). هذا ليس جزءًا من فئة، لذا يمكن وضعه في المستوى الأعلى من الملف، مثل الدالةmain()أو تعريفات الفئات. في الدالة، أنشئAquariumومرِّر إليهWaterSupply. بما أنّ المَعلمةwaterSupplyعامة، يجب تحديد النوع بين قوسين زاويين<>.
fun genericsExample() {
val aquarium = Aquarium<TapWater>(TapWater())
}- في
genericsExample()، يمكن لرمزك الوصول إلىwaterSupplyفي حوض السمك. بما أنّ نوعه هوTapWater، يمكنك استدعاءaddChemicalCleaners()بدون أي عمليات تحويل للأنواع.
fun genericsExample() {
val aquarium = Aquarium<TapWater>(TapWater())
aquarium.waterSupply.addChemicalCleaners()
}- عند إنشاء العنصر
Aquarium، يمكنك إزالة الأقواس الزاوية والمحتوى بينها لأنّ Kotlin يتضمّن ميزة استنتاج النوع. لذلك، ليس هناك سبب لاستخدامTapWaterمرتين عند إنشاء المثيل. يمكن استنتاج النوع من خلال الوسيط إلىAquarium، وسيظل ينشئAquariumمن النوعTapWater.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
aquarium.waterSupply.addChemicalCleaners()
}- لمعرفة ما يحدث، اطبع
needsProcessingقبل وبعد استدعاءaddChemicalCleaners(). في ما يلي الدالة المكتملة.
fun genericsExample() {
val aquarium = Aquarium<TapWater>(TapWater())
println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
aquarium.waterSupply.addChemicalCleaners()
println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
}- أضِف الدالة
main()لاستدعاءgenericsExample()، ثم شغِّل برنامجك ولاحظ النتيجة.
fun main() {
genericsExample()
}⇒ water needs processing: true water needs processing: false
الخطوة 3: تحديد نطاق البحث بشكل أكبر
تعني السمة "عام" أنّه يمكنك تمرير أي قيمة تقريبًا، وهذا يمثّل مشكلة في بعض الأحيان. في هذه الخطوة، يمكنك تحديد Aquarium بشكل أكثر دقة بشأن ما يمكنك وضعه فيه.
- في
genericsExample()، أنشئAquarium، مع تمرير سلسلة لـwaterSupply، ثم اطبع السمةwaterSupplyالخاصة بحوض السمك.
fun genericsExample() {
val aquarium2 = Aquarium("string")
println(aquarium2.waterSupply)
}- شغِّل برنامجك ولاحظ النتيجة.
⇒ string
والنتيجة هي السلسلة التي تم تمريرها، لأنّ Aquarium لا يفرض أي قيود على T.يمكن تمرير أي نوع، بما في ذلك String.
- في
genericsExample()، أنشئAquariumآخر، مع تمريرnullإلىwaterSupply. إذا كانت قيمةwaterSupplyفارغة، اطبع"waterSupply is null".
fun genericsExample() {
val aquarium3 = Aquarium(null)
if (aquarium3.waterSupply == null) {
println("waterSupply is null")
}
}- نفِّذ برنامجك ولاحظ النتيجة.
⇒ waterSupply is null
لماذا يمكنني اجتياز null عند إنشاء Aquarium؟ هذا ممكن لأنّ T يمثّل تلقائيًا النوع Any? القابل للتصغير، وهو النوع في أعلى التسلسل الهرمي للأنواع. ما يلي يعادل ما كتبته سابقًا.
class Aquarium<T: Any?>(val waterSupply: T)- لعدم السماح بتمرير
null، اجعلTمن النوعAnyبشكل صريح، وذلك عن طريق إزالة?بعدAny.
class Aquarium<T: Any>(val waterSupply: T)في هذا السياق، يُطلق على Any اسم قيد عام. وهذا يعني أنّه يمكن تمرير أي نوع من أجل T طالما أنّه ليس null.
- ما تريده حقًا هو التأكّد من أنّه لا يمكن تمرير سوى
WaterSupply(أو إحدى فئاته الفرعية) إلىT. استبدِلAnyبـWaterSupplyلتحديد قيد عام أكثر تحديدًا.
class Aquarium<T: WaterSupply>(val waterSupply: T)الخطوة 4: إضافة المزيد من عمليات التحقّق
في هذه الخطوة، ستتعرّف على الدالة check() للمساعدة في ضمان عمل الرمز البرمجي على النحو المتوقّع. الدالة check() هي دالة مكتبة عادية في Kotlin. تعمل هذه الدالة كتأكيد وستعرض IllegalStateException إذا تم تقييم وسيطتها على أنّها false.
- أضِف طريقة
addWater()إلى فئةAquariumلإضافة الماء، معcheck()يضمن عدم الحاجة إلى معالجة الماء أولاً.
class Aquarium<T: WaterSupply>(val waterSupply: T) {
fun addWater() {
check(!waterSupply.needsProcessing) { "water supply needs processing first" }
println("adding water from $waterSupply")
}
}في هذه الحالة، إذا كانت قيمة needsProcessing صحيحة، سيؤدي check() إلى حدوث استثناء.
- في
genericsExample()، أضِف رمزًا لإنشاءAquariumباستخدامLakeWater، ثم أضِف بعض الماء إليه.
fun genericsExample() {
val aquarium4 = Aquarium(LakeWater())
aquarium4.addWater()
}- شغِّل برنامجك، وسيظهر لك استثناء لأنّ الماء يحتاج إلى فلترة أولاً.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first
at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)- أضِف خطوة لفلترة المياه قبل إضافتها إلى
Aquarium. الآن، عند تشغيل برنامجك، لن يتم عرض أي استثناء.
fun genericsExample() {
val aquarium4 = Aquarium(LakeWater())
aquarium4.waterSupply.filter()
aquarium4.addWater()
}⇒ adding water from generics.LakeWater@880ec60
تتناول المعلومات الواردة أعلاه أساسيات الأنواع العامة. تغطّي المهام التالية المزيد، ولكن المفهوم المهم هو كيفية تعريف واستخدام فئة عامة مع قيد عام.
في هذه المهمة، ستتعرّف على أنواع in وout مع الأنواع العامة. النوع in هو نوع يمكن تمريره إلى فئة فقط، وليس إرجاعه. النوع out هو نوع لا يمكن إرجاعه إلا من فئة.
إذا نظرت إلى الفئة Aquarium، ستلاحظ أنّه لا يتم عرض النوع العام إلا عند الحصول على السمة waterSupply. لا تتوفّر أي طرق تأخذ قيمة من النوع T كمَعلمة (باستثناء تحديدها في الدالة الإنشائية). تتيح لك لغة Kotlin تحديد out أنواع لهذا الغرض تحديدًا، ويمكنها استنتاج معلومات إضافية حول الأماكن التي يمكن فيها استخدام الأنواع بأمان. وبالمثل، يمكنك تحديد أنواع in للأنواع العامة التي يتم تمريرها فقط إلى الطرق، وليس إرجاعها. يسمح ذلك للغة Kotlin بإجراء عمليات فحص إضافية لضمان أمان الرمز البرمجي.
النوعان in وout هما توجيهات لنظام أنواع Kotlin. إنّ شرح نظام الأنواع بالكامل يخرج عن نطاق هذا البرنامج التدريبي (فهو معقّد إلى حدّ ما)، ومع ذلك، سيضع المترجم علامة على الأنواع التي لم يتم وضع العلامة in وout عليها بشكل مناسب، لذا عليك التعرّف عليها.
الخطوة 1: تحديد نوع "خارج"
- في فئة
Aquarium، غيِّرT: WaterSupplyإلى النوعout.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
...
}- في الملف نفسه، خارج الفئة، عرِّف الدالة
addItemTo()التي تتوقّعAquariumمنWaterSupply.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")- اتّصِل بـ
addItemTo()منgenericsExample()وشغِّل برنامجك.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
addItemTo(aquarium)
}⇒ item added
يمكن أن تضمن لغة Kotlin أنّ addItemTo() لن تنفّذ أي إجراء غير آمن من حيث النوع مع النوع العام WaterSupply، لأنّه تم تعريفه على أنّه نوع out.
- إذا أزلت الكلمة الرئيسية
out، سيُظهر المحول البرمجي خطأ عند استدعاءaddItemTo()، لأنّ Kotlin لا يمكنها التأكّد من أنّك لا تفعل أي شيء غير آمن مع النوع.
الخطوة 2: تحديد نوع in
يشبه النوع in النوع out، ولكن للأنواع العامة التي يتم تمريرها إلى الدوال فقط، وليس إرجاعها. إذا حاولت عرض نوع in، سيظهر لك خطأ في المترجم. في هذا المثال، ستحدّد نوع in كجزء من واجهة.
- في ملف Aquarium.kt، حدِّد واجهة
Cleanerتأخذ نوعًا عامًاTمقيّدًا بنوعWaterSupply. بما أنّه يتم استخدامه فقط كوسيطة للدالةclean()، يمكنك تحويله إلى مَعلمةin.
interface Cleaner<in T: WaterSupply> {
fun clean(waterSupply: T)
}- لاستخدام واجهة
Cleaner، أنشئ فئةTapWaterCleanerتنفّذCleanerللتنظيفTapWaterعن طريق إضافة مواد كيميائية.
class TapWaterCleaner : Cleaner<TapWater> {
override fun clean(waterSupply: TapWater) = waterSupply.addChemicalCleaners()
}- في الفئة
Aquarium، عدِّلaddWater()لتلقّيCleanerمن النوعT، ونظِّف الماء قبل إضافته.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
fun addWater(cleaner: Cleaner<T>) {
if (waterSupply.needsProcessing) {
cleaner.clean(waterSupply)
}
println("water added")
}
}- عدِّل مثال الرمز
genericsExample()لإنشاءTapWaterCleanerوAquariumمعTapWater، ثم أضِف بعض الماء باستخدام المنظّف. وسيستخدم المنظّف حسب الحاجة.
fun genericsExample() {
val cleaner = TapWaterCleaner()
val aquarium = Aquarium(TapWater())
aquarium.addWater(cleaner)
}ستستخدم Kotlin معلومات النوع in وout للتأكّد من أنّ الرمز البرمجي يستخدم الأنواع العامة بأمان. من السهل تذكُّر Out وin: يمكن تمرير أنواع out إلى الخارج كقيم معروضة، ويمكن تمرير أنواع in إلى الداخل كوسيطات.

إذا أردت التعرّف أكثر على أنواع المشاكل التي تحلّها أنواع البيانات الواردة والصادرة، تتناول المستندات هذه المشاكل بالتفصيل.
في هذه المهمة، ستتعرّف على الدوال العامة ومتى يجب استخدامها. عادةً، يكون إنشاء دالة عامة فكرة جيدة كلما كانت الدالة تأخذ وسيطة من فئة تحتوي على نوع عام.
الخطوة 1: إنشاء دالة عامة
- في generics/Aquarium.kt، أنشئ دالة
isWaterClean()تأخذAquarium. عليك تحديد النوع العام للمَعلمة، وأحد الخيارات هو استخدامWaterSupply.
fun isWaterClean(aquarium: Aquarium<WaterSupply>) {
println("aquarium water is clean: ${aquarium.waterSupply.needsProcessing}")
}ولكن هذا يعني أنّه يجب أن يحتوي Aquarium على مَعلمة من النوع out ليتم استدعاؤها. في بعض الأحيان، تكون السمتان out أو in مقيّدة جدًا لأنّك تحتاج إلى استخدام نوع لكلّ من الإدخال والإخراج. يمكنك إزالة شرط out من خلال جعل الدالة عامة.
- لجعل الدالة عامة، ضَع أقواسًا مثلثة بعد الكلمة الرئيسية
funمع نوع عامTوأي قيود، وفي هذه الحالةWaterSupply. غيِّرAquariumليتم تقييده بواسطةTبدلاً منWaterSupply.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}T هي مَعلمة نوع isWaterClean() يتم استخدامها لتحديد النوع العام لحوض السمك. هذا النمط شائع جدًا، لذا من المستحسن أن تأخذ بعض الوقت لتجربته.
- استدعِ الدالة
isWaterClean()عن طريق تحديد النوع بين قوسَين زاويَين بعد اسم الدالة مباشرةً وقبل الأقواس.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
isWaterClean<TapWater>(aquarium)
}- بسبب استنتاج النوع من الوسيط
aquarium، لا يلزم تحديد النوع، لذا عليك إزالته. شغِّل برنامجك وراقِب الناتج.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
isWaterClean(aquarium)
}⇒ aquarium water is clean: false
الخطوة 2: إنشاء طريقة عامة بنوع مُجسَّد
يمكنك استخدام دوال عامة للطرق أيضًا، حتى في الفئات التي لها نوع عام خاص بها. في هذه الخطوة، ستضيف طريقة عامة إلى Aquarium تتحقّق مما إذا كان لها نوع WaterSupply.
- في الفئة
Aquarium، عرِّف الدالةhasWaterSupplyOfType()التي تأخذ المَعلمة العامةR(يتم استخدامTحاليًا) التي تقتصر علىWaterSupply، وتعرضtrueإذا كانwaterSupplyمن النوعR. هذا يشبه الدالة التي حدّدتها سابقًا، ولكن داخل الفئةAquarium.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R- لاحظ أنّ
Rالأخيرة يظهر تحتها خط أحمر. مرِّر مؤشر الماوس فوقه لمعرفة الخطأ.
- لإجراء فحص
is، عليك إخبار Kotlin بأنّ النوع reified أو حقيقي ويمكن استخدامه في الدالة. لإجراء ذلك، ضَعinlineقبل الكلمة الرئيسيةfun، وreifiedقبل النوع العامR.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is Rبعد إعادة تحديد نوع، يمكنك استخدامه كنوع عادي، لأنّه يصبح نوعًا حقيقيًا بعد التضمين. وهذا يعني أنّه يمكنك إجراء عمليات التحقّق من النوع is.
إذا لم تستخدِم reified هنا، لن يكون النوع "حقيقيًا" بما يكفي ليسمح Kotlin بعمليات التحقّق من is. ويرجع ذلك إلى أنّ الأنواع غير المحدّدة تكون متاحة فقط في وقت الترجمة البرمجية، ولا يمكن لبرنامجك استخدامها في وقت التشغيل. سنتناول هذا الموضوع بمزيد من التفصيل في القسم التالي.
- استخدِم
TapWaterكنوع. كما هو الحال مع استدعاء الدوال العامة، يمكنك استدعاء الطرق العامة باستخدام أقواس الزاوية مع النوع بعد اسم الدالة. نفِّذ برنامجك ولاحظ النتيجة.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.hasWaterSupplyOfType<TapWater>()) // true
}⇒ true
الخطوة 3: إنشاء دوال الإضافة
يمكنك أيضًا استخدام الأنواع المحدّدة للدوال العادية ودوال الإضافة.
- خارج الفئة
Aquarium، عرِّف دالة إضافية فيWaterSupplyباسمisOfType()تتحقّق مما إذا كانWaterSupplyالذي تم تمريره من نوع معيّن، مثلTapWater.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T- استدعِ دالة الإضافة تمامًا مثل الطريقة.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.waterSupply.isOfType<TapWater>())
}⇒ true
باستخدام وظائف الإضافة هذه، لا يهمّ نوع Aquarium (Aquarium أو TowerTank أو أي فئة فرعية أخرى)، طالما أنّها Aquarium. يُعدّ استخدام بنية عرض النجوم طريقة ملائمة لتحديد مجموعة متنوعة من التطابقات. وعند استخدام عرض النجمة، سيحرص Kotlin أيضًا على عدم اتخاذ أي إجراء غير آمن.
- لاستخدام عرض النجوم، ضَع
<*>بعدAquarium. نقلhasWaterSupplyOfType()لتصبح دالة إضافية، لأنّها ليست جزءًا من واجهة برمجة التطبيقات الأساسية فيAquarium.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R- غيِّر المكالمة إلى
hasWaterSupplyOfType()ونفِّذ برنامجك.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.hasWaterSupplyOfType<TapWater>())
}⇒ true
في المثال السابق، كان عليك وضع علامة reified على النوع العام وجعل الدالة inline، لأنّ Kotlin تحتاج إلى معرفة ذلك في وقت التشغيل، وليس في وقت الترجمة فقط.
تستخدم Kotlin جميع الأنواع العامة في وقت الترجمة فقط. يتيح ذلك للمترجم التأكّد من أنّك تنفّذ كل شيء بأمان. عند وقت التشغيل، يتم محو جميع الأنواع العامة، وبالتالي تظهر رسالة الخطأ السابقة بشأن التحقّق من نوع تم محوه.
اتضح أنّ المترجم البرمجي يمكنه إنشاء رمز برمجي صحيح بدون الاحتفاظ بالأنواع العامة حتى وقت التشغيل. ولكن هذا يعني أنّك قد تنفّذ أحيانًا إجراءً، مثل is، على أنواع عامة لا يمكن للمترجم البرمجي التعامل معه. لهذا السبب، أضافت Kotlin أنواعًا مجسَّدة أو حقيقية.
يمكنك الاطّلاع على مزيد من المعلومات حول الأنواع المحدّدة وإزالة الأنواع في مستندات Kotlin.
ركّز هذا الدرس على الأنواع العامة، وهي مهمة لجعل الرمز البرمجي أكثر مرونة وأسهل في إعادة الاستخدام.
- أنشئ فئات عامة لجعل الرمز أكثر مرونة.
- أضِف قيودًا عامة للحدّ من الأنواع المستخدَمة مع الأنواع العامة.
- استخدِم النوعَين
inوoutمع الأنواع العامة لتوفير عملية أفضل للتحقّق من الأنواع من أجل حصر الأنواع التي يتم تمريرها إلى الفئات أو إرجاعها منها. - إنشاء دوال وطرق عامة للعمل مع الأنواع العامة على سبيل المثال:
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... } - استخدِم دوال الإضافة العامة لإضافة وظائف غير أساسية إلى فئة.
- تكون الأنواع المحدّدة ضرورية أحيانًا بسبب محو الأنواع. تستمر الأنواع المحدّدة، على عكس الأنواع العامة، حتى وقت التشغيل.
- استخدِم الدالة
check()للتأكّد من أنّ الرمز البرمجي يعمل على النحو المتوقّع. على سبيل المثال:check(!waterSupply.needsProcessing) { "water supply needs processing first" }
مستندات Kotlin
إذا أردت الحصول على مزيد من المعلومات حول أي موضوع في هذه الدورة التدريبية، أو إذا واجهتك أي مشكلة، يمكنك الانتقال إلى https://kotlinlang.org.
- الأنواع العامة
- القيود العامة
- توقّعات الأداء
- أنواع
Inوout - المَعلمات المحدّدة
- إزالة النوع
- وظيفة
check()
برامج تعليمية حول Kotlin
يتضمّن الموقع الإلكتروني https://try.kotlinlang.org برامج تعليمية غنية بصريًا تُعرف باسم Kotlin Koans، ومترجم مستند إلى الويب، ومجموعة كاملة من المستندات المرجعية مع أمثلة.
دورة Udacity التدريبية
للاطّلاع على دورة Udacity التدريبية حول هذا الموضوع، يُرجى الانتقال إلى برنامج Kotlin التدريبي للمبرمجين.
IntelliJ IDEA
يمكنك العثور على مستندات IntelliJ IDEA على موقع JetBrains الإلكتروني.
يسرد هذا القسم مهامًا منزلية محتملة للطلاب الذين يعملون على هذا الدرس التطبيقي العملي كجزء من دورة تدريبية يقودها مدرّب. على المعلّم تنفيذ ما يلي:
- حدِّد واجبًا منزليًا إذا لزم الأمر.
- توضيح كيفية إرسال الواجبات المنزلية للطلاب
- وضع درجات للواجبات المنزلية
يمكن للمدرّبين استخدام هذه الاقتراحات بالقدر الذي يريدونه، ويجب ألا يترددوا في تكليف الطلاب بأي واجبات منزلية أخرى يرونها مناسبة.
إذا كنت تعمل على هذا الدرس العملي بنفسك، يمكنك استخدام مهام الواجب المنزلي هذه لاختبار معلوماتك.
الإجابة عن هذه الأسئلة
السؤال 1
أي مما يلي هو اصطلاح تسمية نوع عام؟
▢ <Gen>
▢ <Generic>
▢ <T>
▢ <X>
السؤال 2
يُطلق على القيود المفروضة على الأنواع المسموح بها لنوع عام ما يلي:
▢ قيود عامة
▢ قيد عام
▢ توضيح المعلومات
▢ حدّ نوع عام
السؤال 3
تعني إعادة التجسيد ما يلي:
▢ تم احتساب تأثير التنفيذ الفعلي لأحد العناصر.
▢ تم ضبط فهرس إدخال مقيّد على الصف.
▢ تم تحويل مَعلمة النوع العام إلى نوع حقيقي.
▢ تم تشغيل مؤشر خطأ عن بُعد.
انتقِل إلى الدرس التالي:
للحصول على نظرة عامة على الدورة التدريبية، بما في ذلك روابط تؤدي إلى دروس تطبيقية أخرى حول الترميز، يُرجى الاطّلاع على "برنامج Kotlin التدريبي للمبرمجين: مرحبًا بك في الدورة التدريبية".