برنامج تدريب Kotlin للمبرمجين 4: البرمجة المستندة إلى العنصر

يشكّل هذا الدرس التطبيقي التدريبي جزءًا من الدورة التدريبية لبرمجة مطوّري البرامج في Kolin. ستحصل على أقصى قيمة ممكنة من هذه الدورة التدريبية إذا كنت تستخدم الدروس التطبيقية حول الترميز بشكل متسلسل. بناءً على معلوماتك، قد تتمكّن من تصفّح بعض الأقسام. هذه الدورة التدريبية موجّهة للمبرمجين الذين يعرفون لغة موجّهة نحو الأغراض ويريدون تعلّم لغة Kotlin.

مقدمة

في هذا الدرس التطبيقي حول الترميز، يمكنك إنشاء برنامج Kotlin والتعرّف على الصفوف والكائنات في Kotlin. سيكون معظم هذا المحتوى مألوفًا لك إذا كنت تعرف لغة أخرى موجَّهة إلى الكائن، ولكن لدى لغة Kotlin بعض الاختلافات المهمة لتقليل مقدار الرمز الذي تحتاج إلى كتابته. ستتعلّم أيضًا معلومات عن الصفوف المجرّدة وتفويض الواجهة.

بدلاً من إنشاء نموذج تطبيق واحد، تم تصميم الدروس في هذه الدورة التدريبية لبناء معرفتك، ولكن تكون شبه مستقلة عن بعضها البعض ليتسنى لك تصفح الأقسام التي تعرفها. لربطهما معًا، تستخدم العديد من الأمثلة مظهر معرض أحواض السمك. وإذا أردت الاطّلاع على القصة الكاملة لأحواض السمك، يمكنك الاطّلاع على الدورة التدريبية بعنوان Kotlin Botcamp for Programmers Udacity.

ما يجب معرفته

  • أساسيات لغة Kotlin، بما في ذلك الأنواع وعوامل التشغيل وعمليات التكرار
  • بنية دالة Kotlin's
  • أساسيات البرمجة الموجَّهة للعناصر
  • أساسيات IDE مثل IntelliJ IDEA أو Android Studio

ما ستتعرَّف عليه

  • كيفية إنشاء دروس والوصول إلى المواقع في Kotlin
  • كيفية إنشاء واستخدام أدوات إنشاء الصفوف في Kotlin
  • كيفية إنشاء فئة فرعية وآلية اكتسابها
  • لمحة عن الصفوف والتجريد والواجهات
  • كيفية إنشاء فئات البيانات واستخدامها
  • كيفية استخدام الدرجات المفردة، والتعداد، والصفوف المغلقة

الإجراءات التي ستنفذّها

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

يجب أن تكون مصطلحات البرمجة التالية مألوفة لك:

  • الصفوف هي مخططات للعناصر. على سبيل المثال، الفئة Aquarium هي مخطط لصنع كائن أكواريوم.
  • الكائنات هي أمثلة للفئات، كما أن كائن الأكواريوم هو Aquarium فعلي واحد.
  • الخصائص هي خصائص الفئات، مثل طول Aquarium وعرضه وارتفاعه.
  • الطُرق، التي تُعرف أيضًا باسم دوال الأعضاء، هي وظيفة الصف. الطرق هي ما يمكنك قوله مع "الكثير من" على سبيل المثال، يمكنك fillWithWater() استخدام الكائن Aquarium.
  • الواجهة هي مواصفات يمكن للصف تنفيذها. على سبيل المثال، عادة ما يكون التنظيف أمرًا شائعًا بخلاف الأحواض، وغالبًا ما يحدث التنظيف بطرق مماثلة لكائنات مختلفة. لذلك، قد يكون لديك واجهة باسم Clean تحدد طريقة clean(). يستطيع الصف Aquarium تنفيذ الواجهة Clean لتنظيف حوض السمك باستخدام اسفنجة استحمام ناعمة.
  • الحزم هي طريقة لتجميع الرموز ذات الصلة للحفاظ على تنظيمها أو لإنشاء مكتبة من الرموز. بعد إنشاء حزمة، يمكنك استيراد محتوى الحزمة إلى ملف آخر وإعادة استخدام الرمز والصفوف فيه.

في هذه المهمة، يمكنك إنشاء حزمة جديدة وفئة تحتوي على بعض الخصائص والطريقة.

الخطوة 1: إنشاء حزمة

تساعدك الحِزم في الحفاظ على تنظيم الرمز.

  1. في جزء المشروع، ضمن مشروع مرحبًا Kotlin، انقر بزر الماوس الأيمن على المجلد src.
  2. اختَر New > Package (جديد &gt؛ اسم الحزمة) وامنحها اسم example.myapp.

الخطوة 2: إنشاء صف باستخدام الخصائص

يتم تحديد الصفوف بالكلمات الرئيسية class، في حين تبدأ أسماء الفئات بحرف كبير.

  1. انقر بزر الماوس الأيمن على الحزمة example.myapp.
  2. اختَر New > Kotlin File / Class.
  3. ضِمن النوع، اختَر الصف الدراسي، ثم أدخِل اسمًا للصف Aquarium. يتضمن IntelliJ IDEA اسم الحزمة في الملف وينشئ فئة Aquarium فارغة لك.
  4. داخل الفئة Aquarium، حدِّد خصائص var لإعدادها وارتفاعها وارتفاعها (بالسنتيمتر) وإعدادها. يجب إعداد المواقع بالقيم التلقائية.
package example.myapp

class Aquarium {
    var width: Int = 20
    var height: Int = 40
    var length: Int = 100
}

ضمن الخيارات المتقدمة، تُنشئ لغة Kotlin تلقائيًا أدوات القياس والضبط للخصائص التي حدّدتها في فئة Aquarium، حتى تتمكن من الوصول إلى المواقع مباشرةً، على سبيل المثال، myAquarium.length.

الخطوة 3: إنشاء دالة main()

أنشئ ملفًا جديدًا باسم main.kt للاحتفاظ بوظيفة main().

  1. في لوحة المشروع على يمين الصفحة، انقر بزر الماوس الأيمن على الحزمة example.myapp.
  2. اختَر New > Kotlin File / Class.
  3. ضمن القائمة المنسدلة Kind (النوع)، احتفظ بالاختيار ملف، ثم أدخِل اسمًا للملف main.kt. يتضمن IntelliJ IDEA اسم الحزمة، ولكنه لا يتضمن تعريف الفئة للملف.
  4. اختَر دالة buildAquarium() وفي داخلها إنشاء مثيل Aquarium. لإنشاء مثيل، ارجع إلى الفئة كما لو كانت دالة، Aquarium(). وهذا يستدعي دالة الصف وينشئ مثالاً للصف Aquarium، على غرار استخدام new بلغات أخرى.
  5. اختَر دالة main() واستدعِ buildAquarium().
package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium()
}

fun main() {
    buildAquarium()
}

الخطوة 4: إضافة طريقة

  1. في صف Aquarium، أضِف طريقة لطباعة خصائص حوض الأسماك والأكواريوم.
    fun printSize() {
        println("Width: $width cm " +
                "Length: $length cm " +
                "Height: $height cm ")
    }
  1. في تطبيق main.kt، في buildAquarium()، يمكنك الاتصال بالطريقة printSize() على myAquarium.
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
}
  1. شغِّل برنامجك بالنقر على المثلث الأخضر بجانب دالة main(). راقب النتيجة.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
  1. في buildAquarium()، أضِف الرمز لضبط الارتفاع على 60 واطبع خصائص البُعد التي تم تغييرها.
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
    myAquarium.height = 60
    myAquarium.printSize()
}
  1. شغِّل برنامجك ولاحظ النتائج.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 100 cm Height: 60 cm 

في هذه المهمة، يمكنك إنشاء دالة إنشاء للصف ومواصلة العمل باستخدام الخصائص.

الخطوة 1: إنشاء ملف شخصي

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

في بعض لغات البرمجة، يتم تعريف التصميم عن طريق إنشاء طريقة داخل الصف تحمل الاسم نفسه للصف الدراسي. في لغة Kotlin، يمكنك تحديد طريقة وضع الملف مباشرةً في تعريف الصف نفسه، مع تحديد المعلمات داخل الأقواس كما لو كانت الفئة طريقة. وكما هو الحال مع الدوال في لغة Kotlin، يمكن أن تتضمن هذه المعلمات قيمًا تلقائية.

  1. في الصف Aquarium الذي أنشأته سابقًا، غيِّر تعريف الفئة لتضمين ثلاث معلّمات داعم مع قيم تلقائية للسمة length وwidth وheight، وخصِّصها للخصائص المقابلة لها.
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
   // Dimensions in cm
   var length: Int = length
   var width: Int = width
   var height: Int = height
...
}
  1. وتتميّز لغة Kotlin بأنها أصغر حجمًا من خلال تحديد المواقع التي تتضمّن أدوات الإنشاء مباشرةً، وذلك باستخدام var أو val، كما تعمل لغة Kotlin أيضًا على إنشاء أدوات القياس والضبط. وبعد ذلك، يمكنك إزالة تعريفات الخصائص في نص الصف الدراسي.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
  1. عند إنشاء كائن Aquarium باستخدام طريقة وضع التصميم هذه، لا يمكنك تحديد أي وسيطات والحصول على قيم تلقائية أو تحديد بعض منها فقط، أو تحديد كل منها وإنشاء كائن Aquarium بحجم كامل. في الدالة buildAquarium()، جرِّب طرقًا مختلفة لإنشاء كائن Aquarium باستخدام المعلّمات المسماة.
fun buildAquarium() {
    val aquarium1 = Aquarium()
    aquarium1.printSize()
    // default height and length
    val aquarium2 = Aquarium(width = 25)
    aquarium2.printSize()
    // default width
    val aquarium3 = Aquarium(height = 35, length = 110)
    aquarium3.printSize()
    // everything custom
    val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
    aquarium4.printSize()
}
  1. شغِّل البرنامج ولاحظ النتائج.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 25 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 110 cm Height: 35 cm 
Width: 25 cm Length: 110 cm Height: 35 cm 

تجدر الإشارة إلى أنك لم تضطر إلى زيادة أعباء المُنشئ وصياغة نسخة مختلفة لكل من هذه الحالات (بالإضافة إلى بعض الأعداد الأخرى للنُسخ الأخرى). تنشئ لغة البرمجة Kotlin ما هو مطلوب من القيم التلقائية والمعلّمات المحدّدة.

الخطوة 2: إضافة كائن init

وتنص نماذج الإنشاء أعلاه على الخصائص فقط وتخصص قيمة تعبير لها. إذا كانت العارضة بحاجة إلى رمز إعداد إضافي، يمكن وضعها في قالب init واحد أو أكثر. في هذه الخطوة، يمكنك إضافة بعض قوالب init إلى صف واحد (Aquarium).

  1. في الفئة Aquarium، أضِف كتلة init لطباعة العنصر الذي يجري إعداده، وجزء آخر لطباعة الحجم باللتر.
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
    init {
        println("aquarium initializing")
    }
    init {
        // 1 liter = 1000 cm^3
        println("Volume: ${width * length * height / 1000} l")
    }
}
  1. شغِّل البرنامج ولاحظ النتائج.
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm 
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm 

تجدر الإشارة إلى أنّه يتم تنفيذ قوالب init بالترتيب الذي تظهر به في تعريف الفئة، ويتم تنفيذها جميعًا عند استدعاء المنشئ.

الخطوة 3: التعرّف على أدوات الإنشاء الثانوية

وفي هذه الخطوة، يمكنك التعرّف على أدوات الإنشاء الثانوية وإضافة واحدة إلى صفك. بالإضافة إلى طريقة الإنشاء الأساسية التي يمكن أن تحتوي على قالب init واحد أو أكثر، يمكن أن تتضمن فئة Kotlin أيضًا واحدة أو أكثر من أدوات الإنشاء الثانوية للسماح بإنشاء حِزم وضع المُنشئ طريقة مختلفة، أي الداعمات المختلفة التي لها وسيطات مختلفة.

  1. في الصف Aquarium، أضِف دالة إنشاء ثانوية تستخدم عددًا من الأسماك كوسيطة لها، باستخدام الكلمة الرئيسية constructor. يمكنك إنشاء خاصية خزّان val بحجم الأحواض المحسوبة باللتر استنادًا إلى عدد الأسماك. لنفترض أن هناك 2 لتر من المياه لكل سمكة، (2000 سم^3)، بالإضافة إلى مساحة إضافية حتى لا يتسرب المياه.
constructor(numberOfFish: Int) : this() {
    // 2,000 cm^3 per fish + extra room so water doesn't spill
    val tank = numberOfFish * 2000 * 1.1
}
  1. داخل طريقة الإنشاء الثانوية، احتفِظ بالطول والعرض (الذي تم تحديده في أداة الإنشاء الأساسية) واحسب الارتفاع اللازم لجعل الخزان بمستوى الصوت المحدّد.
    // calculate the height needed
    height = (tank / (length * width)).toInt()
  1. في الدالة buildAquarium()، يمكنك إضافة استدعاء لإنشاء Aquarium باستخدام دالة الإنشاء الثانوية الجديدة. اطبع الحجم والحجم.
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
  1. شغِّل برنامجك ولاحظ النتائج.
⇒ aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

يُرجى ملاحظة أن الحجم يُطبَّق مرّتين، مرّة حسب الكتلة init في المُنشئ الأساسي قبل تنفيذ المُنشئ الثانوي، والثانية من خلال الرمز في buildAquarium().

يمكنك تضمين الكلمة الرئيسية constructor في أداة الإنشاء الأساسية أيضًا، ولكنها ليست ضرورية في معظم الحالات.

الخطوة 4: إضافة أداة إرجاع قيمة جديدة للموقع

في هذه الخطوة، يمكنك إضافة إرجاع قيمة فاضحة للموقع. تحدّد لغة Kotlin تلقائيًا أدوات الاستدعاء والاستدعاءات عند تحديد الخصائص، ولكن في بعض الأحيان يجب تعديل قيمة أحد المواقع أو احتسابها. مثلاً، تمت طباعة حجم Aquarium أعلاه. يمكنك توفير الحجم كأحد المواقع من خلال تحديد متغيّر وداعٍ له. بما أنّ volume يجب حسابها، يجب أن تعرِض القيمة الدالة القيمة المحسوبة التي يمكنك تنفيذها باستخدام دالة من سطر واحد.

  1. في الفئة Aquarium، حدِّد خاصية Int تُسمى volume، وحدِّد طريقة get() التي تحتسب الحجم في السطر التالي.
val volume: Int
    get() = width * height * length / 1000  // 1000 cm^3 = 1 l
  1. إزالة حظر init الذي يطبع مستوى الصوت.
  2. يُرجى إزالة الرمز من خلال buildAquarium() الذي يطبع المجلد.
  3. في طريقة printSize()، أضِف سطرًا لطباعة الحجم.
fun printSize() {
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm "
    )
    // 1 l = 1000 cm^3
    println("Volume: $volume l")
}
  1. شغِّل برنامجك ولاحظ النتائج.
⇒ aquarium initializing
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

الأبعاد والحجم متطابقان تمامًا كما في السابق، ولكن لا تتم طباعة الحجم إلا مرة واحدة بعد إعداد الكائن بشكل كامل بواسطة كل من طريقة الإنشاء الأساسية وأداة الإنشاء الثانوية.

الخطوة 5: إضافة أداة ضبط للمواقع

في هذه الخطوة، يمكنك إنشاء أداة ضبط مواقع جديدة للحجم.

  1. في الصف Aquarium، عليك تغيير السمة volume إلى سمة var حتى يتم ضبطها أكثر من مرة.
  2. أضِف مقياسًا لسمتَي volume عن طريق إضافة طريقة set() أسفل وحدة التلقيم، والتي تعيد حساب الارتفاع بناءً على كمية المياه التي يتم توفيرها. وفقًا للاصطلاح، يكون اسم المعلّمة setter هو value، ولكن يمكنك تغييره إذا كنت تفضّل ذلك.
var volume: Int
    get() = width * height * length / 1000
    set(value) {
        height = (value * 1000) / (width * length)
    }
  1. في buildAquarium()، أضِف الرمز لضبط مستوى صوت الأكواريوم على 70 لترًا. اطبع الحجم الجديد.
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    aquarium6.volume = 70
    aquarium6.printSize()
}
  1. شغِّل البرنامج مرة أخرى ولاحظ الارتفاع والارتفاع اللذين تم تغييرهما.
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm 
Volume: 70 l

لم تتوفّر أي تعديلات حتى الآن، مثل public أو private، في الرمز. هذا، ويعود السبب في ذلك إلى أن كل شيء في لغة Kotlin يكون تلقائيًا بشكل تلقائي، أي أنه يمكن الوصول إلى كل شيء من أي مكان، بما في ذلك الصفوف والطرق والخصائص ومتغيرات الأعضاء.

في لغة Kotlin، يمكن أن تحتوي الفئات والكائنات والواجهات ودوال البناء والدوال والمواقع والخصائص وضبطها على معدّلات ظهور:

  • وتعني القيمة public خارج الصف. ويكون كل محتوى متاحًا للجميع تلقائيًا، بما في ذلك المتغيّرات وطرق الصف.
  • وتعني القيمة internal أنها ستكون مرئية داخل هذه الوحدة فقط. الوحدة هي مجموعة من ملفات Kotlin التي يتم تجميعها معًا، مثل مكتبة أو تطبيق.
  • ويعني private أنّ الملف سيكون مرئيًا فقط في تلك الفئة (أو ملف المصدر إذا كنت تعمل مع الدوال).
  • protected هو نفسه private، لكنه سيكون مرئيًا أيضًا لأي فئات فرعية.

اطّلع على مفاتيح تعديل مستوى الرؤية في وثائق Kotlin لمزيد من المعلومات.

متغيرات العضو

وتكون الخصائص داخل الفئة أو المتغيّرات الأعضاء public تلقائيًا. إذا حدّدتها باستخدام var، ستكون قابلة للتغيير، أي قابلة للقراءة والكتابة. وإذا حدّدتها باستخدام val، ستكون للقراءة فقط بعد الإعداد.

إذا كنت تريد موقعًا يمكن للرمز قراءته أو كتابته، ولكن يمكن للرمز الخارجي قراءته فقط، يمكنك ترك الموقع والمُعرّف له على "علني" والتصريح عن "setter" (خاص) كما هو موضح أدناه.

var volume: Int
    get() = width * height * length / 1000
    private set(value) {
        height = (value * 1000) / (width * length)
    }

في هذه المهمة، ستتعرّف على آلية عمل الفئات الفرعية والتوريث في لغة Kotlin. وهي تشبه ما رأيته بلغات أخرى، ولكن هناك بعض الاختلافات.

وبشكل تلقائي، لا يمكن تقسيم الصفوف إلى فئات فرعية بلغة Kotlin تلقائيًا. وبالمثل، لا يمكن تجاوز الخصائص ومتغيرات الأعضاء من خلال الفئات الفرعية (على الرغم من إمكانية الوصول إليها).

يجب وضع علامة على صف باعتباره open للسماح بتصنيفه إلى فئات فرعية. وبالمثل، يجب وضع علامة على الخصائص ومتغيرات الأعضاء على أنها open، لإلغاء هذه القيم في الفئة الفرعية. الكلمة الرئيسية open مطلوبة لمنع تسرب تفاصيل التنفيذ عن طريق الخطأ كجزء من واجهة الصف.

الخطوة 1: فتح درس الأكواريوم

في هذه الخطوة، يمكنك إنشاء صف Aquarium open، حتى يمكنك تجاوزه في الخطوة التالية.

  1. ضَع علامة على الفئة Aquarium وجميع مواقعها باستخدام الكلمة الرئيسية open.
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
    open var volume: Int
        get() = width * height * length / 1000
        set(value) {
            height = (value * 1000) / (width * length)
        }
  1. أضف خاصية shape مفتوحة بالقيمة "rectangle".
   open val shape = "rectangle"
  1. أضِف خاصية water المفتوحة التي تحتوي على دَربة تعرض 90% من حجم Aquarium.
    open var water: Double = 0.0
        get() = volume * 0.9
  1. يمكنك إضافة رمز إلى طريقة printSize() لطباعة الشكل ومقدار الماء كنسبة مئوية من الحجم.
fun printSize() {
    println(shape)
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm ")
    // 1 l = 1000 cm^3
    println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
  1. في buildAquarium()، غيِّر الرمز لإنشاء Aquarium باستخدام width = 25 وlength = 25 وheight = 40.
fun buildAquarium() {
    val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
    aquarium6.printSize()
}
  1. شغِّل برنامجك ولاحظ النتيجة الجديدة.
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)

الخطوة 2: إنشاء فئة فرعية

  1. يمكنك إنشاء فئة فرعية من Aquarium تُسمى TowerTank، والتي تستخدم خزانًا أسطوانيًا مستديرًا بدلاً من خزان مستطيل. يمكنك إضافة TowerTank أقل من Aquarium، لأنه يمكنك إضافة صف آخر في الملف نفسه كصف Aquarium.
  2. في TowerTank، يمكنك إلغاء الخاصية height التي يتم تعريفها في دالة الإنشاء. لإلغاء موقع إلكتروني، استخدِم الكلمة الرئيسية override في الفئة الفرعية.
  1. جعل أداة إنشاء TowerTank تتخذ diameter. استخدِم diameter لكل من length وwidth عند استدعاء دالة الإنشاء في الفئة العليا على Aquarium.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
  1. تجاوز خاصية الحجم لحساب أسطوانة. صيغة الأسطوانة هي باي مضروبًا في نصف قطر القسمة في الارتفاع. يجب استيراد قيمة PI الثابتة من java.lang.Math.
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }
  1. في TowerTank، عليك إلغاء خاصية water لتكون 80% من مستوى الصوت.
override var water = volume * 0.8
  1. إلغاء shape لتصبح "cylinder".
override val shape = "cylinder"
  1. من المفترض أن يبدو الصف الأخير من TowerTank مثل الرمز أدناه.

Aquarium.kt:

package example.myapp

import java.lang.Math.PI

... // existing Aquarium class

class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }

    override var water = volume * 0.8
    override val shape = "cylinder"
}
  1. في buildAquarium()، أنشئ TowerTank بقطر 25 سم وارتفاع 45 سم. اطبع الحجم.

main.kt:

package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium(width = 25, length = 25, height = 40)
    myAquarium.printSize()
    val myTower = TowerTank(diameter = 25, height = 40)
    myTower.printSize()
}
  1. شغِّل برنامجك ولاحظ النتائج.
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)
aquarium initializing
cylinder
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 18 l Water: 14.4 l (80.0% full)

في بعض الأحيان، قد ترغب في تحديد سلوكيات أو خصائص مشتركة ستتم مشاركتها بين بعض الصفوف ذات الصلة. يقدم Kotlin طريقتين لإجراء ذلك، الواجهات والصفوف تجريدية. في هذه المَهمّة، يمكنك إنشاء فئة مجرّدة من AquariumFish للخصائص المشتركة بين جميع الأسماك. يمكنك إنشاء واجهة باسم FishAction لتحديد السلوك المشترك مع جميع الأسماك.

  • لا يمكن إنشاء مثيل لفئة مجردة أو واجهة بشكل مستقل، مما يعني أنه لا يمكنك إنشاء عناصر من هذه الأنواع مباشرةً.
  • تحتوي الصفوف التجريدية على أدوات إنشاء.
  • لا يمكن أن تحتوي الواجهات على أي منطق إنشاء أو تخزّن أي حالة.

الخطوة الأولى: إنشاء صف تجريدي

  1. ضمن example.myapp، أنشئ ملفًا جديدًا، AquariumFish.kt.
  2. إنشاء صف دراسي، يُسمّى أيضًا باسم AquariumFish، ووضع علامة عليه باستخدام abstract.
  3. أضِف خاصية String واحدة (color)، وضَع علامة عليها باستخدام abstract.
package example.myapp

abstract class AquariumFish {
    abstract val color: String
}
  1. أنشئ فئتين فرعيتين من AquariumFish وShark وPlecostomus.
  2. نظرًا لأن color مجرّد، على الفئات الفرعية تنفيذه. ضبط Shark على اللون الرمادي وPlecostomus من الذهب
class Shark: AquariumFish() {
    override val color = "gray"
}

class Plecostomus: AquariumFish() {
    override val color = "gold"
}
  1. في main.kt، أنشِئ دالة makeFish() لاختبار صفوفك. أنشئ مثيلًا للسمة Shark وPlecostomus، ثم اطبع لون كل منها.
  2. يمكنك حذف الرمز التجريبي السابق في main() وإضافة مكالمة إلى makeFish(). ويجب أن يظهر الرمز كما يلي.

main.kt:

package example.myapp

fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()

    println("Shark: ${shark.color}")
    println("Plecostomus: ${pleco.color}")
}

fun main () {
    makeFish()
}
  1. شغِّل برنامجك ولاحظ النتائج.
⇒ Shark: gray 
Plecostomus: gold

يمثل المخطّط التالي الصفّة Shark والصف Plecostomus، الذي يُصنّف الفئة الفرعية المجرّدة AquariumFish.

رسم بياني يوضّح الفئة التجريدية، وأكواريوم "أسماك السمك"، بالإضافة إلى فئتَين فرعيّتَين، وهما "أسماك القرش" و"بليوكوموس".

الخطوة الثانية: إنشاء واجهة

  1. في Aquaiumfish.kt، أنشئ واجهة باسم FishAction باستخدام eat().
interface FishAction  {
    fun eat()
}
  1. أضف FishAction إلى كل فئة من الفئات الفرعية، ونفِّذ eat() من خلال طباعتها في عمل الأسماك.
class Shark: AquariumFish(), FishAction {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

class Plecostomus: AquariumFish(), FishAction {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. في الدالة makeFish()، اطلُب من كل سمكة أنشأتها أن تنشد طعامًا عن طريق استدعاء eat().
fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()
    println("Shark: ${shark.color}")
    shark.eat()
    println("Plecostomus: ${pleco.color}")
    pleco.eat()
}
  1. شغِّل برنامجك ولاحظ النتائج.
⇒ Shark: gray
hunt and eat fish
Plecostomus: gold
eat algae

يمثل الرسم البياني التالي كل من الفئة Shark والصف Plecostomus، وكلاهما تتألف من واجهة FishAction وتنفذها.

حالات استخدام الصفوف التجريدية مقابل الواجهات

الأمثلة الموضحة أعلاه بسيطة، ولكن عندما يكون لديك عدد كبير من الصفوف المترابطة، يمكن أن تساعدك الصفوف التجريدية والواجهات على الحفاظ على نظافة تصميمك وأكثر تنظيمًا وأسهل في الصيانة.

كما هو موضّح أعلاه، يمكن أن تتضمّن الصفوف المجرّدة طرق وضع، ولا يمكن إنشاء الواجهات، ولكنها متشابهة جدًا بخلاف ذلك. متى يجب استخدامها؟

عند استخدام واجهات لإنشاء صف، يتم توسيع وظائف الصف الدراسي من خلال مثيلات الصفوف التي تحتوي عليها. غالبًا ما تسهّل المقطوعة الموسيقية عملية إعادة استخدام الرموز والسبب وراء اكتسابها من فئة مجردة. ويمكنك أيضًا استخدام واجهات متعددة في صف دراسي، ولكن لا يمكنك استخدام سوى فئة فرعية من صف تجريدي واحد.

وكثيرًا ما تؤدي التركيبة إلى تحسين التغليف والاقتران الأقل (الاعتمادية) والواجهات الأكثر وضوحًا وقابلية استخدام أكبر للرمز. لهذه الأسباب، يكون استخدام التركيبة مع الواجهات هو التصميم المفضّل. من ناحية أخرى، غالبًا ما يكون التوارث من فئة مجردة مناسبًا لبعض المشاكل. ولهذا السبب، ننصحك بتفضيل التعرّف على المقطوعة الموسيقية، ولكن عندما يكون السبب مفهومًا كبيرًا في لغة البرمجة Kotlin، يمكنك استخدامها أيضًا.

  • استخدم واجهة إذا كان لديك الكثير من الطرق وعملية تنفيذ واحدة أو عمليتين تلقائيتين، على سبيل المثال كما في AquariumAction أدناه.
interface AquariumAction {
    fun eat()
    fun jump()
    fun clean()
    fun catchFish()
    fun swim()  {
        println("swim")
    }
}
  • استخدم صفًا تجريديًا في أي وقت لا يمكنك فيه إكمال صف دراسي. على سبيل المثال، بالرجوع إلى الصف AquariumFish، يمكنك تطبيق AquariumFish على FishAction، وتقديم تنفيذ تلقائي للسمة eat مع ترك تجريد color، بسبب عدم توفّر لون تلقائي للسمك.
interface FishAction  {
    fun eat()
}

abstract class AquariumFish: FishAction {
   abstract val color: String
   override fun eat() = println("yum")
}

تضمّنت المهمة السابقة صفوفًا مجرّدة وواجهات وأفكارًا تتعلّق بالمقطوعة الموسيقية. تفويض الواجهة هو أسلوب متقدم يتم فيه تنفيذ طرق الواجهة من خلال كائن مساعد (أو مفوّض)، والذي تستخدمه الصفّة بعد ذلك. يمكن أن يكون هذا الأسلوب مفيدًا عند استخدام واجهة في سلسلة من الصفوف غير ذات الصلة: تضيف وظيفة الواجهة المطلوبة إلى فئة مساعد منفصلة، وتستخدم كل صفة مثالاً للصف المساعد لتنفيذ الوظيفة.

في هذه المَهمّة، يمكنك استخدام تفويض الواجهة لإضافة وظائف إلى صف.

الخطوة الأولى: إنشاء واجهة جديدة

  1. في Aquaiumfish.kt، أزِل الفئة AquariumFish. بدلاً من التوارث من الفئة AquariumFish، سيطبّق Plecostomus وShark واجهات لكلٍّ من إجراء السمك ولونه.
  2. أنشئ واجهة جديدة، FishColor، تحدد اللون كسلسلة.
interface FishColor {
    val color: String
}
  1. يمكنك تغيير Plecostomus لتنفيذ واجهتَين، FishAction وFishColor. يجب إلغاء color من FishColor وeat() من FishAction.
class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. غيّر فئة Shark أيضًا لتنفيذ الواجهتين، FishAction وFishColor، بدلاً من اكتسابها من AquariumFish.
class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}
  1. ويجب أن يظهر الرمز النهائي على النحو التالي:
package example.myapp

interface FishAction {
    fun eat()
}

interface FishColor {
    val color: String
}

class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}

class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

الخطوة 2: إنشاء صف فردي

بعد ذلك، يمكنك تنفيذ إعداد جزء التفويض عن طريق إنشاء صف مساعد يطبِّق FishColor. يمكنك إنشاء صف أساسي باسم GoldColor ينفّذ FishColor، وكل ما يعنيه أن لونه هو الذهب.

ليس من المنطقي إنشاء مثيلات متعددة من GoldColor، لأنّها جميعًا تنفِّذ الإجراء نفسه. وبالتالي، تتيح لك لغة Kotlin الإعلان عن صف دراسي يمكنك إنشاء مثيل واحد منه باستخدام الكلمة الرئيسية object بدلاً من class. سينشئ Kotlin هذا المثيل، ويُشار إليه على اسم الفئة. بعد ذلك، يمكن لجميع الكائنات الأخرى استخدام هذه النسخة الافتراضية فقط - ليس هناك طريقة لإنشاء مثيلات أخرى من هذه الفئة. وإذا كنت معتادًا على نمط فردي، هذه هي الطريقة التي تطبّق بها درجات اللون الفردي في Kotlin.

  1. في Aquaiumfish.kt، أنشئ كائنًا لـ GoldColor. يمكنك إلغاء اللون.
object GoldColor : FishColor {
   override val color = "gold"
}

الخطوة 3: إضافة تفويض للواجهة من أجل أسماك اللون

أنت الآن على استعداد لاستخدام تفويض الواجهة.

  1. في Aquaiumfish.kt، أزِل إلغاء color من Plecostomus.
  2. غيِّر الفئة Plecostomus للحصول على لونها من GoldColor. ويمكنك إجراء ذلك من خلال إضافة by GoldColor إلى بيان الصف الدراسي وإنشاء التفويض. بناءً على ذلك، بدلاً من تنفيذ FishColor، استخدِم عملية التنفيذ المقدّمة من GoldColor. وبالتالي في كل مرة يتم فيها الوصول إلى color، يتم تفويضها إلى GoldColor.
class Plecostomus:  FishAction, FishColor by GoldColor {
   override fun eat() {
       println("eat algae")
   }
}

وكما هو الحال مع هذه الصفوف، ستكون جميع نباتات Plecos ذهبية اللون، ولكن هذه السمكة متوفّرة بالفعل بألوان متعددة. يمكنك معالجة هذه المشكلة من خلال إضافة معلمة إنشاء للون مع GoldColor باعتباره اللون التلقائي لـ Plecostomus.

  1. يمكنك تغيير الفئة Plecostomus لاجتياز الفئة في fishColor باستخدام دالة الإنشاء، وضبط الإعداد التلقائي على GoldColor. تغيير التفويض من by GoldColor إلى by fishColor.
class Plecostomus(fishColor: FishColor = GoldColor):  FishAction,
       FishColor by fishColor {
   override fun eat() {
       println("eat algae")
   }
}

الخطوة 4: إضافة تفويض بواجهة تطبيق fishAction

يمكنك أيضًا استخدام تفويض واجهة المستخدم FishAction.

  1. في Aquaiumfish.kt أنشئ صف PrintingFishAction ينفّذ FishAction، الذي يستغرق String، ثم food، ثم يطبع السمكة.
class PrintingFishAction(val food: String) : FishAction {
    override fun eat() {
        println(food)
    }
}
  1. في الفئة Plecostomus، عليك إزالة وظيفة الإلغاء eat()، لأنه سيتم استبدالها بتفويض.
  2. في بيان Plecostomus، يمكنك تفويض FishAction إلى PrintingFishAction، وتجاوز "eat algae".
  3. مع كل هذا التفويض، ليس هناك رمز في نص فئة Plecostomus، لذا عليك إزالة {}، لأنه يتم التعامل مع جميع عمليات الإلغاء من خلال تفويض الواجهة
class Plecostomus (fishColor: FishColor = GoldColor):
        FishAction by PrintingFishAction("eat algae"),
        FishColor by fishColor

يوضّح المخطّط التالي فئتَي Shark وPlecostomus، واللتان تتألفان من واجهتي PrintingFishAction وFishColor، ولكنهما تفوّضان التنفيذ.

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

تتشابه فئة البيانات مع struct في بعض اللغات الأخرى، حيث تتوفّر في الأساس للاحتفاظ ببعض البيانات، ولكن لا يزال عنصر فئة البيانات عبارة عن عنصر. تتمتع عناصر فئة بيانات Kotlin ببعض المزايا الإضافية، مثل المرافق للطباعة والنسخ. في هذه المهمة، يمكنك إنشاء فئة بيانات بسيطة والتعرّف على الدعم الذي يوفّره Kotlin لصفوف البيانات.

الخطوة 1: إنشاء صف بيانات

  1. أضِف حزمة decor جديدة ضمن حزمة example.myapp للاحتفاظ بالرمز الجديد. انقر بزر الماوس الأيمن على example.myapp في جزء المشروع واختَر File > New > Package.
  2. في الحزمة، أنشِئ صفًا جديدًا باسم Decoration.
package example.myapp.decor

class Decoration {
}
  1. لجعل Decoration فئة بيانات، ابدأ بيان الصف بالكلمة الرئيسية data.
  2. يجب إضافة السمة String التي تحمل الاسم rocks لتوفير بعض البيانات للصف.
data class Decoration(val rocks: String) {
}
  1. في الملف خارج الصف، أضِف دالة makeDecorations() لإنشاء مثيل Decoration من خلال "granite" وطباعته.
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)
}
  1. أضِف دالة main() لاستدعاء makeDecorations() ونفِّذ برنامجك. لاحظ الإخراج المعقول الذي تم إنشاؤه لأنه عبارة عن فئة بيانات.
⇒ Decoration(rocks=granite)
  1. في makeDecorations()، عليك إنشاء مثيلين لكائن Decoration آخرَين من نوع واحد، وهما: "& Slate:"
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)

    val decoration2 = Decoration("slate")
    println(decoration2)

    val decoration3 = Decoration("slate")
    println(decoration3)
}
  1. في makeDecorations()، أضِف بيانًا مطبوعًا يطبع نتيجة مقارنة decoration1 بـ decoration2، وثانيًا يقارن decoration3 مع decoration2. استخدِم الإجراء equals() الذي تقدّمه فئات البيانات.
    println (decoration1.equals(decoration2))
    println (decoration3.equals(decoration2))
  1. شغِّل الرمز.
⇒ Decoration(rocks=granite)
Decoration(rocks=slate)
Decoration(rocks=slate)
false
true

الخطوة الثانية: استخدام المحور

للوصول إلى خصائص عنصر بيانات وإسنادها إلى المتغيرات، يمكنك تخصيص واحد تلو الآخر، مثل هذا.

val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver

بدلاً من ذلك، يمكنك إنشاء متغيّرات واحدة لكل موقع، وتحديد كائن البيانات لمجموعة المتغيّرات. يضع Kotlin قيمة السمة في كل متغير.

val (rock, wood, diver) = decoration

ويُعرف هذا الإجراء باسم التدمير وهو اختصار مختصر مفيد. يجب أن يتطابق عدد المتغيرات مع عدد الخصائص، ويتم تخصيص المتغيرات بالترتيب الذي تم تعريفها به في الفئة. إليك مثال كامل يمكنك استخدامه في Decoration.kt.

// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}

fun makeDecorations() {
    val d5 = Decoration2("crystal", "wood", "diver")
    println(d5)

// Assign all properties to variables.
    val (rock, wood, diver) = d5
    println(rock)
    println(wood)
    println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver)
crystal
wood
diver

إذا كنت لا تحتاج إلى خاصية واحدة أو أكثر من المواقع، يمكنك تخطيها باستخدام _ بدلاً من اسم المتغير، كما هو موضّح في الرمز أدناه.

    val (rock, _, diver) = d5

في هذه المهمة، ستتعرّف على بعض دروس الغرض الخاص في لغة Kotlin، بما في ذلك ما يلي:

  • دروس فردية
  • عمليات التعداد
  • دروس مغلقة

الخطوة 1: استرجاع صفوف مفردة

تذكّر المثال السابق باستخدام الصف GoldColor.

object GoldColor : FishColor {
   override val color = "gold"
}

ونظرًا لأن كل مثيل من GoldColor ينفذ الإجراء نفسه، يتم تعريفه على أنه object بدلاً من class للإشارة إلى فردي. لا يمكن أن يتضمّن الملف أكثر من نسخة واحدة.

الخطوة 2: إنشاء تعداد

ويتيح أيضًا لغة Kotlin التعداد الذي يتيح لك تعدّد الأشياء والإشارة إليها بالاسم، كما هو الحال في اللغات الأخرى. يمكنك الإعلان عن تعداد عن طريق إضافة بادئة إلى الكلمة الرئيسية enum. يحتاج تعريف التعداد الأساسي إلى قائمة أسماء فقط، ولكن يمكنك أيضًا تحديد حقل واحد أو أكثر مرتبط بكل اسم.

  1. في Decoration.kt، جرّب مثالًا تعدادًا.
enum class Color(val rgb: Int) {
   RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}

تعدّ التعداد إلى حد ما درجة أفرد، حيث يمكن أن تكون هناك قيمة واحدة فقط، وقيمة واحدة فقط من كل قيمة في التعداد. على سبيل المثال، لا يمكن أن تكون هناك أكثر من Color.RED وColor.GREEN وColor.BLUE واحد. في هذا المثال، يتم تحديد قيم نموذج أحمر أخضر أزرق (RGB) للسمة rgb لتمثيل مكوّنات الألوان. يمكنك أيضًا الحصول على القيمة العددية لمجموعة تعداد باستخدام السمة ordinal واسمها باستخدام السمة name.

  1. جرِّب مثالاً آخر للإحصاء.
enum class Direction(val degrees: Int) {
    NORTH(0), SOUTH(180), EAST(90), WEST(270)
}

fun main() {
    println(Direction.EAST.name)
    println(Direction.EAST.ordinal)
    println(Direction.EAST.degrees)
}
⇒ EAST
2
90

الخطوة 3: إنشاء صف مغلق

الصف المُغلق هو فئة يمكن تقسيمها فرعيًا، ولكن داخل الملف الذي تم الإعلان عنه فقط. إذا حاولت إنشاء فئة فرعية في ملف مختلف، ستظهر لك رسالة خطأ.

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

  1. في Aquaiumfish.kt، يمكنك تجربة مثال لفئة مغلقة في الحفاظ على المظهر المائي.
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()

fun matchSeal(seal: Seal): String {
   return when(seal) {
       is Walrus -> "walrus"
       is SeaLion -> "sea lion"
   }
}

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

تناول هذا الدرس جوانب كثيرة من الموضوع. وفي حين أن جزءًا كبيرًا من لغة البرمجة Kotlin يجب أن يكون مألوفًا من لغات البرمجة الأخرى التي تركّز على الكائنات، فإن لغة Kotlin تضيف بعض الميزات للحفاظ على شفرة موجزة وسهلة القراءة.

الصفوف ودوال البناء

  • يمكنك تحديد صف باللغة Kotlin باستخدام class.
  • تنشئ لغة Kotlin تلقائيًا أدوات الضبط والدعامات للخصائص.
  • حدِّد المنشئ الأساسي في تعريف الفئة مباشرةً. على سبيل المثال:
    class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
  • إذا كانت دالة الإنشاء الأساسية بحاجة إلى رمز إضافي، يمكنك كتابتها في مجموعة init أو أكثر.
  • يمكن أن تُحدِّد الفئة طريقة تصميم ثانوية أو أكثر باستخدام constructor، ولكن نمط Kotlin هو استخدام دالة المصنع بدلاً من ذلك.

معدِّلات مستوى الرؤية والفئات الفرعية

  • كل الصفوف والوظائف في لغة Kotlin هي public تلقائيًا، ولكن يمكنك استخدام المعدِّلات لتغيير مستوى الرؤية إلى internal أو private أو protected.
  • لإنشاء فئة فرعية، يجب وضع علامة على الفئة الرئيسية open.
  • لإلغاء الطرق والخصائص في فئة فرعية، يجب وضع علامة على الطرق والخصائص open في الفئة الرئيسية.
  • ولا يمكن تصنيف الفئة المغلقة إلا في الملف نفسه الذي يتم تحديدها فيه. أدخِل صفًا مختومًا عن طريق وضع البادئة sealed في البيان.

فئات البيانات وعدد الحالات الفردية والتعداد

  • أنشِئ فئة بيانات من خلال إضافة بادئة إلى التعريف data.
  • التدمير هو اختصاص لتخصيص خصائص كائن data لفصل المتغيرات.
  • أنشِئ صفًا فرديًا باستخدام object بدلاً من class.
  • تحديد تعداد باستخدام enum class

الصفوف التجريدية والواجهات والتفويض

  • تُعد الصفوف والواجهات التجريدية طريقتين لمشاركة السلوك المشترك بين الصفوف.
  • تحدد الفئة المجرّدة الخصائص والسلوك، ولكنها تترك عملية التنفيذ للفئات الفرعية.
  • تحدِّد الواجهة السلوك، وقد توفّر عمليات تنفيذ تلقائية لبعض السلوكيات أو جميعها.
  • عند استخدام واجهات لإنشاء صف، يتم توسيع وظائف الصف الدراسي من خلال مثيلات الصفوف التي تحتوي عليها.
  • يستخدم تفويض الواجهة تركيبًا، ولكن أيضًا يفوّض التنفيذ إلى صفوف الواجهة.
  • تشكّل المقطوعة الموسيقية طريقة فعالة لإضافة وظائف إلى الصف باستخدام تفويض الواجهة. من المفضّل عادةً استخدام التركيبة العامة، ولكن قد يكون التوارث من فئة مجرّدة أكثر ملاءمةً لبعض المشاكل.

مستندات Kotlin

إذا كنت بحاجة إلى مزيد من المعلومات عن أي موضوع في هذه الدورة التدريبية، أو إذا واجهتك مشكلة، يمكنك البدء باستخدام https://kotlinlang.org.

برامج تعليمية بلغة Kotlin

يتضمّن الموقع الإلكتروني https://try.kotlinlang.org برامج تعليمية غنية اسمها Kotlin Koans ومترجمًا فوريًا مستندًا إلى الويب ومجموعة كاملة من المستندات المرجعية التي تتضمّن أمثلة.

دورة Udacity التدريبية

للاطّلاع على دورة Udacity التدريبية حول هذا الموضوع، يُرجى الاطّلاع على برنامج Kotlin Campus للمبرمجين.

IntelliJ IDEA

يمكن العثور على مستندات IntelliJ IDEA على الموقع الإلكتروني JetBrains.

يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:

  • يمكنك تخصيص واجب منزلي إذا لزم الأمر.
  • التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
  • وضع درجات للواجبات المنزلية.

ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.

إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.

الإجابة عن هذه الأسئلة

السؤال 1

تستخدم الصفوف طريقة خاصة تعمل كمخطط لإنشاء العناصر من هذا الصف. ما اسم الطريقة؟

▢ مقاول إنشاء المباني

▢ منشئ مثيل فوري

▢ صانع البناء

▢ مخطط

السؤال 2

أي من العبارات التالية غير صحيحة عن الواجهات والصفوف التجريدية؟

▢ يمكن أن تحتوي الصفوف التجريدية على أدوات إنشاء.

▢ لا يمكن أن تتضمن الواجهات أدوات تصميم.

▢ يمكن إنشاء مثيل للواجهات والصفوف التجريدية مباشرة.

▢ يجب تطبيق الخصائص المجرّدة في الفئات الفرعية للفئة المجرّدة.

السؤال 3

أيّ مما يلي ليس مُعدِّل مستوى رؤية Kotlin للمواقع والأساليب وما إلى ذلك؟

internal

nosubclass

protected

private

السؤال 4

يمكنك وضع فئة البيانات هذه في الاعتبار:
data class Fish(val name: String, val species:String, val colors:String)
أي مما يلي ليس رمزًا صالحًا لإنشاء عنصر Fish وإزالته؟

val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")

val (name2, _, colors2) = Fish("Bitey", "shark", "gray")

val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")

val (name4, species4, colors4) = Fish("Harry", "halibut")

السؤال 5

لنفترض أنك تمتلك حديقة حيوانات تضم الكثير من الحيوانات التي تحتاج إلى الاعتناء بها جميعًا. أيّ مما يلي لن يكون جزءًا من تنفيذ الرعاية؟

interface لأنواع مختلفة من الأطعمة التي تتناولها الحيوانات.

▢ صف abstract Caretaker يمكنك من خلاله إنشاء أنواع مختلفة من مقدِّمي الرعاية.

interface لتوفير مياه نظيفة لحيوان

▢ صف data لأحد الإدخالات في جدول التغذية.

انتقل إلى الدرس التالي: 5.1 الإضافات

للحصول على نظرة عامة على الدورة التدريبية، بما في ذلك الروابط إلى مختبرات ترميز أخرى، يُرجى الاطّلاع على "Kotlin Botcamp للمبرمجين: مرحبًا بك في الدورة التدريبية&"