هذا الدرس العملي حول الترميز هو جزء من دورة Kotlin التدريبية للمبرمجين. يمكنك تحقيق أقصى استفادة من هذه الدورة التدريبية إذا اتبعت ترتيب الخطوات في دروس البرمجة. استنادًا إلى معرفتك، قد تتمكّن من قراءة بعض الأقسام بسرعة. هذه الدورة التدريبية موجّهة للمبرمجين الذين يعرفون لغة برمجة تعتمد على العناصر ويريدون تعلُّم Kotlin.
مقدمة
في هذا الدرس التطبيقي حول الترميز، ستنشئ برنامجًا بلغة Kotlin وتتعرّف على الفئات والكائنات في Kotlin. سيكون معظم هذا المحتوى مألوفًا لك إذا كنت تعرف لغة أخرى موجّهة للكائنات، ولكن لدى Kotlin بعض الاختلافات المهمة لتقليل مقدار الرمز الذي تحتاج إلى كتابته. ستتعرّف أيضًا على الفئات المجردة وتفويض الواجهة.
بدلاً من إنشاء تطبيق نموذجي واحد، تم تصميم الدروس في هذه الدورة التدريبية لتعزيز معرفتك، ولكنها شبه مستقلة عن بعضها البعض حتى تتمكن من تصفّح الأقسام التي تعرفها. ولربطها ببعضها، تستخدم العديد من الأمثلة سمة حوض السمك. إذا أردت الاطّلاع على قصة حوض السمك بالكامل، يمكنك الرجوع إلى دورة Kotlin Bootcamp للمبرمجين على Udacity.
ما يجب معرفته
- أساسيات Kotlin، بما في ذلك الأنواع والعوامل والتكرار
- بنية الدوال في Kotlin
- أساسيات البرمجة التي تعتمد على العناصر
- أساسيات بيئة التطوير المتكاملة (IDE) مثل IntelliJ IDEA أو "استوديو Android"
أهداف الدورة التعليمية
- كيفية إنشاء فئات والوصول إلى الخصائص في Kotlin
- كيفية إنشاء الدوال الإنشائية للفئات واستخدامها في Kotlin
- كيفية إنشاء فئة فرعية وطريقة عمل الوراثة
- لمحة عن الفئات المجردة والواجهات وتفويض الواجهات
- كيفية إنشاء فئات البيانات واستخدامها
- كيفية استخدام الكائنات الفردية وقيم التعداد والفئات المحكمة
الإجراءات التي ستنفذّها
- إنشاء فئة تتضمّن سمات
- إنشاء دالة إنشائية لفئة
- إنشاء فئة فرعية
- فحص أمثلة على الفئات المجردة والواجهات
- إنشاء فئة بيانات بسيطة
- مزيد من المعلومات حول الكائنات الفردية والتعدادات والفئات المحكمة
من المفترض أنّك على دراية بمصطلحات البرمجة التالية:
- الفئات هي مخططات للكائنات. على سبيل المثال، فئة
Aquarium
هي المخطط الأساسي لإنشاء كائن حوض سمك. - الكائنات هي مثيلات للفئات، وكائن حوض السمك هو
Aquarium
واحد. - الخصائص هي سمات الفئات، مثل طول
Aquarium
وعرضه وارتفاعه. - الطرق، التي تُعرف أيضًا باسم الدوال الأعضاء، هي وظائف الفئة. الطُرق هي الإجراءات التي يمكنك "تنفيذها" باستخدام الكائن. على سبيل المثال، يمكنك
fillWithWater()
عنصرAquarium
. - الواجهة هي مواصفات يمكن أن تنفّذها الفئة. على سبيل المثال، التنظيف هو سمة مشتركة بين الكائنات الأخرى غير أحواض السمك، ويتم التنظيف بشكل عام بطرق متشابهة للكائنات المختلفة. لذا، يمكنك إنشاء واجهة باسم
Clean
تحدّد طريقةclean()
. يمكن للفئةAquarium
تنفيذ الواجهةClean
لتنظيف حوض السمك باستخدام إسفنجة ناعمة. - الحِزم هي طريقة لتجميع الرموز البرمجية ذات الصلة من أجل الحفاظ على تنظيمها أو إنشاء مكتبة من الرموز البرمجية. بعد إنشاء حزمة، يمكنك استيراد محتوى الحزمة إلى ملف آخر وإعادة استخدام الرموز والفئات الموجودة فيها.
في هذه المهمة، ستنشئ حزمة جديدة وفئة تتضمّن بعض الخصائص والدوال.
الخطوة 1: إنشاء حزمة
يمكن أن تساعدك الحِزم في تنظيم الرموز البرمجية.
- في لوحة المشروع، ضِمن مشروع Hello Kotlin، انقر بزر الماوس الأيمن على المجلد src.
- اختَر جديد > حزمة وأطلِق عليها اسم
example.myapp
.
الخطوة 2: إنشاء فئة تتضمّن سمات
يتم تحديد الفئات باستخدام الكلمة الرئيسية class
، وتبدأ أسماء الفئات اصطلاحًا بحرف كبير.
- انقر بزر الماوس الأيمن على حزمة example.myapp.
- اختَر جديد > ملف / فئة Kotlin.
- ضمن النوع، اختَر الصف، وأدخِل اسم الصف
Aquarium
. يتضمّن IntelliJ IDEA اسم الحزمة في الملف وينشئ لك فئةAquarium
فارغة. - داخل الفئة
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()
.
- في لوحة المشروع على يمين الشاشة، انقر بزر الماوس الأيمن على حزمة example.myapp.
- اختَر جديد > ملف / فئة Kotlin.
- في القائمة المنسدلة النوع، أبقِ الخيار ملف، وأطلِق على الملف الاسم
main.kt
. يتضمّن IntelliJ IDEA اسم الحزمة، ولكنّه لا يتضمّن تعريف فئة لملف. - حدِّد الدالة
buildAquarium()
وأنشئ داخلها مثيلاً منAquarium
. لإنشاء مثيل، يمكنك الرجوع إلى الفئة كما لو كانت دالة،Aquarium()
. يؤدي ذلك إلى استدعاء الدالة الإنشائية للفئة وإنشاء مثيل للفئةAquarium
، على غرار استخدامnew
في لغات أخرى. - حدِّد الدالة
main()
واستدعِ الدالةbuildAquarium()
.
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium()
}
fun main() {
buildAquarium()
}
الخطوة 4: إضافة طريقة
- في الفئة
Aquarium
، أضِف طريقة لطباعة خصائص أبعاد حوض السمك.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
}
- في
main.kt
، فيbuildAquarium()
، استدعِ طريقةprintSize()
فيmyAquarium
.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
}
- نفِّذ برنامجك من خلال النقر على المثلث الأخضر بجانب الدالة
main()
. لاحظ النتيجة.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm
- في
buildAquarium()
، أضِف رمزًا لضبط الارتفاع على 60 وطباعة خصائص الأبعاد المتغيرة.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
myAquarium.height = 60
myAquarium.printSize()
}
- شغِّل برنامجك وراقِب الناتج.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 100 cm Height: 60 cm
في هذه المهمة، ستنشئ دالة إنشاء للفئة، وستواصل العمل مع الخصائص.
الخطوة 1: إنشاء دالة إنشائية
في هذه الخطوة، ستضيف دالة إنشاء إلى الفئة Aquarium
التي أنشأتها في المهمة الأولى. في المثال السابق، يتم إنشاء كل مثيل من Aquarium
بالأبعاد نفسها. يمكنك تغيير الأبعاد بعد إنشائها من خلال ضبط الخصائص، ولكن سيكون من الأسهل إنشاءها بالحجم الصحيح منذ البداية.
في بعض لغات البرمجة، يتم تحديد الدالة الإنشائية من خلال إنشاء طريقة ضمن الفئة لها الاسم نفسه. في Kotlin، يمكنك تحديد الدالة الإنشائية مباشرةً في تعريف الفئة نفسه، وتحديد المَعلمات داخل أقواس كما لو كانت الفئة دالة. وكما هو الحال مع الدوال في Kotlin، يمكن أن تتضمّن هذه المَعلمات قيمًا تلقائية.
- في الفئة
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
...
}
- تتمثّل طريقة Kotlin الأكثر اختصارًا في تحديد السمات مباشرةً باستخدام الدالة الإنشائية، وذلك باستخدام
var
أوval
، كما تنشئ Kotlin دوال الجلب والتعديل تلقائيًا. بعد ذلك، يمكنك إزالة تعريفات السمة في نص الفئة.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
- عند إنشاء عنصر
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()
}
- شغِّل البرنامج ولاحظ الناتج.
⇒ 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
.
- في الفئة
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")
}
}
- شغِّل البرنامج ولاحظ الناتج.
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 أيضًا على دالة إنشائية ثانوية واحدة أو أكثر للسماح بتعدّد تحميل الدالة الإنشائية، أي الدوال الإنشائية التي تحتوي على وسيطات مختلفة.
- في الفئة
Aquarium
، أضِف دالة إنشاء ثانوية تأخذ عددًا من الأسماك كمعلَمة، وذلك باستخدام الكلمة الرئيسيةconstructor
. أنشئ سمةval
لخزان الأسماك من أجل حساب حجم حوض السمك باللتر استنادًا إلى عدد الأسماك. افترض أنّ كل سمكة تحتاج إلى لترَين (2,000 سم^3) من الماء، بالإضافة إلى مساحة إضافية صغيرة حتى لا ينسكب الماء.
constructor(numberOfFish: Int) : this() {
// 2,000 cm^3 per fish + extra room so water doesn't spill
val tank = numberOfFish * 2000 * 1.1
}
- داخل الدالة الإنشائية الثانوية، احتفظ بالطول والعرض (اللذين تم ضبطهما في الدالة الإنشائية الأساسية) كما هما، واحتسِب الارتفاع المطلوب لجعل الخزان بالوحدة المحدّدة.
// calculate the height needed
height = (tank / (length * width)).toInt()
- في الدالة
buildAquarium()
، أضِف طلبًا لإنشاءAquarium
باستخدام الدالة الإنشائية الثانوية الجديدة. اطبع الحجم ومستوى الصوت.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
- شغِّل برنامجك وراقِب الناتج.
⇒ aquarium initializing Volume: 80 l Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
لاحظ أنّه تتم طباعة الحجم مرتين، مرة واحدة بواسطة الحظر init
في الدالة الإنشائية الأساسية قبل تنفيذ الدالة الإنشائية الثانوية، ومرة واحدة بواسطة الرمز في buildAquarium()
.
كان بإمكانك أيضًا تضمين الكلمة الرئيسية constructor
في الدالة الإنشائية الأساسية، ولكن ليس ذلك ضروريًا في معظم الحالات.
الخطوة 4: إضافة دالة جلب موقع إلكتروني جديد
في هذه الخطوة، يمكنك إضافة دالة جلب صريحة للسمة. تحدّد لغة Kotlin تلقائيًا دوال الجلب والضبط عند تحديد السمات، ولكن في بعض الأحيان يجب تعديل قيمة السمة أو احتسابها. على سبيل المثال، في الأعلى، طبعت حجم Aquarium
. يمكنك إتاحة مستوى الصوت كسمة من خلال تحديد متغيّر ودالة getter له. بما أنّه يجب احتساب volume
، يجب أن تعرض الدالة getter القيمة المحسوبة، ويمكنك إجراء ذلك باستخدام دالة من سطر واحد.
- في الفئة
Aquarium
، حدِّد السمةInt
باسمvolume
، وحدِّد الطريقةget()
التي تحسب الحجم في السطر التالي.
val volume: Int
get() = width * height * length / 1000 // 1000 cm^3 = 1 l
- أزِلوا كتلة
init
التي تطبع مستوى الصوت. - أزِل الرمز في
buildAquarium()
الذي يطبع مستوى الصوت. - في طريقة
printSize()
، أضِف سطرًا لطباعة مستوى الصوت.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm "
)
// 1 l = 1000 cm^3
println("Volume: $volume l")
}
- شغِّل برنامجك وراقِب الناتج.
⇒ aquarium initializing Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
الأبعاد والحجم هي نفسها كما كانت من قبل، ولكن لا تتم طباعة الحجم إلا مرة واحدة بعد أن يتم تهيئة الكائن بالكامل من خلال كل من الدالة الإنشائية الأساسية والدالة الإنشائية الثانوية.
الخطوة 5: إضافة أداة ضبط للسمة
في هذه الخطوة، يمكنك إنشاء أداة ضبط جديدة للسمة الخاصة بمستوى الصوت.
- في الفئة
Aquarium
، غيِّرvolume
إلىvar
حتى يمكن ضبطه أكثر من مرة. - أضِف أداة ضبط للسمة
volume
من خلال إضافة الطريقةset()
أسفل أداة الجلب، والتي تعيد احتساب الارتفاع استنادًا إلى كمية المياه المتوفّرة. حسب الاصطلاح، يكون اسم مَعلمة أداة الضبط هوvalue
، ولكن يمكنك تغييره إذا كنت تفضّل ذلك.
var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- في
buildAquarium()
، أضِف رمزًا لضبط حجم حوض السمك على 70 لترًا. اطبع الحجم الجديد.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
aquarium6.volume = 70
aquarium6.printSize()
}
- نفِّذ برنامجك مرة أخرى ولاحظ التغييرات في الارتفاع ومستوى الصوت.
⇒ 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
، ستكون للقراءة فقط بعد التهيئة.
إذا كنت تريد خاصية يمكن لرمزك البرمجي قراءتها أو كتابتها، ولكن يمكن للرمز البرمجي الخارجي قراءتها فقط، يمكنك ترك الخاصية ودالة الجلب كعنصرَين عامَّين وتحديد دالة الضبط كعنصر خاص، كما هو موضّح أدناه.
var volume: Int
get() = width * height * length / 1000
private set(value) {
height = (value * 1000) / (width * length)
}
في هذه المهمة، ستتعرّف على طريقة عمل الفئات الفرعية والميراث في Kotlin. وهي تشبه ما رأيته بلغات أخرى، ولكن مع بعض الاختلافات.
في Kotlin، لا يمكن إنشاء فئات فرعية من الفئات تلقائيًا. وبالمثل، لا يمكن إلغاء خصائص ومتغيرات الأعضاء بواسطة الفئات الفرعية (على الرغم من إمكانية الوصول إليها).
يجب وضع علامة open
على فئة للسماح بتصنيفها إلى فئات فرعية. وبالمثل، يجب وضع علامة open
على السمات ومتغيّرات الأعضاء لتجاوزها في الفئة الفرعية. مطلوب استخدام الكلمة الرئيسية open
لمنع تسريب تفاصيل التنفيذ عن طريق الخطأ كجزء من واجهة الفئة.
الخطوة 1: جعل صف Aquarium مفتوحًا
في هذه الخطوة، يمكنك إنشاء الفئة Aquarium
open
، حتى تتمكّن من تجاهلها في الخطوة التالية.
- ضع الكلمة الرئيسية
open
بجانب الفئةAquarium
وجميع سماتها.
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)
}
- أضِف السمة
shape
المفتوحة مع القيمة"rectangle"
.
open val shape = "rectangle"
- أضِف السمة
water
المفتوحة مع دالة جلب تعرض% 90 من حجمAquarium
.
open var water: Double = 0.0
get() = volume * 0.9
- أضِف رمزًا إلى طريقة
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)")
}
- في
buildAquarium()
، غيِّر الرمز لإنشاءAquarium
باستخدامwidth = 25
وlength = 25
وheight = 40
.
fun buildAquarium() {
val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
aquarium6.printSize()
}
- نفِّذ برنامجك ولاحظ الناتج الجديد.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full)
الخطوة 2: إنشاء فئة فرعية
- أنشئ فئة فرعية من
Aquarium
باسمTowerTank
، والتي تنفّذ خزانًا أسطوانيًا مستديرًا بدلاً من خزان مستطيل. يمكنك إضافةTowerTank
أسفلAquarium
، لأنّه يمكنك إضافة صف آخر في الملف نفسه الذي يتضمّن الصفAquarium
. - في
TowerTank
، يمكنك تجاهل السمةheight
التي تم تحديدها في الدالة الإنشائية. لإلغاء إحدى السمات، استخدِم الكلمة الرئيسيةoverride
في الفئة الفرعية.
- اجعل الدالة الإنشائية الخاصة بـ
TowerTank
تأخذdiameter
. استخدِمdiameter
لكل منlength
وwidth
عند استدعاء الدالة الإنشائية في الفئة الرئيسيةAquarium
.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
- تجاوز السمة "الحجم" لاحتساب أسطوانة. صيغة الأسطوانة هي "باي" مضروبًا في مربع نصف القطر مضروبًا في الارتفاع. عليك استيراد الثابت
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()
}
- في
TowerTank
، استبدِل السمةwater
بنسبة% 80 من مستوى الصوت.
override var water = volume * 0.8
- تجاهل قيمة
shape
واستخدام"cylinder"
override val shape = "cylinder"
- يجب أن تبدو فئة
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"
}
- في
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()
}
- شغِّل برنامجك وراقِب الناتج.
⇒ 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
. - أنشئ فئة، تُعرف أيضًا باسم
AquariumFish
، وضع علامةabstract
عليها. - أضِف سمة
String
واحدة، وهيcolor
، وضع علامةabstract
عليها.
package example.myapp
abstract class AquariumFish {
abstract val color: String
}
- أنشئ فئتَين فرعيتَين من
AquariumFish
وShark
وPlecostomus
. - بما أنّ
color
مجرّد، يجب أن تنفّذه الفئات الفرعية. اجعلShark
باللون الرمادي وPlecostomus
باللون الذهبي.
class Shark: AquariumFish() {
override val color = "gray"
}
class Plecostomus: AquariumFish() {
override val color = "gold"
}
- في ملف main.kt، أنشئ الدالة
makeFish()
لاختبار صفوفك. أنشئ مثيلاً منShark
وPlecostomus
، ثم اطبع لون كل منهما. - احذف رمز الاختبار السابق في
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()
}
- شغِّل برنامجك وراقِب الناتج.
⇒ Shark: gray Plecostomus: gold
يمثّل الرسم البياني التالي الفئة Shark
والفئة Plecostomus
، وهما فئتان فرعيتان من الفئة المجردة AquariumFish
.
الخطوة 2: إنشاء واجهة
- في ملف AquariumFish.kt، أنشئ واجهة باسم
FishAction
تتضمّن طريقةeat()
.
interface FishAction {
fun eat()
}
- أضِف
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")
}
}
- في الدالة
makeFish()
، اجعل كل سمكة أنشأتها تأكل شيئًا ما عن طريق استدعاءeat()
.
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
shark.eat()
println("Plecostomus: ${pleco.color}")
pleco.eat()
}
- شغِّل برنامجك وراقِب الناتج.
⇒ 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: إنشاء واجهة جديدة
- في ملف AquariumFish.kt، أزِل الفئة
AquariumFish
. بدلاً من الوراثة من الفئةAquariumFish
، ستنفّذ الفئتانPlecostomus
وShark
واجهات لكل من إجراء السمكة ولونها. - أنشئ واجهة جديدة،
FishColor
، تحدّد اللون كسلسلة.
interface FishColor {
val color: String
}
- غيِّر
Plecostomus
لتنفيذ واجهتَين،FishAction
، وFishColor
. عليك تجاهلcolor
منFishColor
وeat()
منFishAction
.
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- غيِّر فئة
Shark
لتنفيذ الواجهتينFishAction
وFishColor
أيضًا بدلاً من الوراثة منAquariumFish
.
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
- يجب أن تبدو التعليمات البرمجية المكتملة على النحو التالي:
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 مثيلاً واحدًا، ويتم الرجوع إلى هذا المثيل من خلال اسم الفئة. بعد ذلك، يمكن لجميع العناصر الأخرى استخدام هذا المثال فقط، ولا يمكن إنشاء أمثلة أخرى من هذه الفئة. إذا كنت على دراية بنمط Singleton، إليك كيفية تنفيذ هذا النمط في Kotlin.
- في ملف AquariumFish.kt، أنشئ عنصرًا لـ
GoldColor
. تجاوز اللون
object GoldColor : FishColor {
override val color = "gold"
}
الخطوة 3: إضافة تفويض الواجهة إلى FishColor
أنت الآن جاهز لاستخدام تفويض الواجهة.
- في ملف AquariumFish.kt، أزِل عملية الإلغاء لـ
color
منPlecostomus
. - غيِّر الفئة
Plecostomus
للحصول على لونها منGoldColor
. يمكنك إجراء ذلك عن طريق إضافةby GoldColor
إلى تعريف الفئة، ما يؤدي إلى إنشاء التفويض. هذا يعني أنّه بدلاً من تنفيذFishColor
، عليك استخدام عملية التنفيذ التي يوفّرهاGoldColor
. لذا، في كل مرة يتم فيها الوصول إلىcolor
، يتم تفويضGoldColor
.
class Plecostomus: FishAction, FishColor by GoldColor {
override fun eat() {
println("eat algae")
}
}
في الصف الحالي، سيكون لون جميع أسماك Plecos ذهبيًا، ولكن في الواقع، تتوفّر هذه الأسماك بألوان عديدة. يمكنك حلّ هذه المشكلة عن طريق إضافة مَعلمة دالة إنشائية للّون مع GoldColor
كاللّون التلقائي لـ Plecostomus
.
- غيِّر فئة
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
.
- في ملف AquariumFish.kt، أنشئ فئة
PrintingFishAction
تنفّذ الواجهةFishAction
، وتأخذString
وfood
، ثم تطبع ما تأكله السمكة.
class PrintingFishAction(val food: String) : FishAction {
override fun eat() {
println(food)
}
}
- في الفئة
Plecostomus
، أزِل وظيفة التجاوزeat()
، لأنّك ستستبدلها بوظيفة تفويض. - في تعريف
Plecostomus
، فوِّضFishAction
إلىPrintingFishAction
، مع تمرير"eat algae"
. - مع كل هذا التفويض، لا يوجد رمز في نص الفئة
Plecostomus
، لذا أزِل{}
، لأنّ جميع عمليات الإلغاء تتم معالجتها من خلال تفويض الواجهة.
class Plecostomus (fishColor: FishColor = GoldColor):
FishAction by PrintingFishAction("eat algae"),
FishColor by fishColor
يمثّل الرسم البياني التالي الفئتين Shark
وPlecostomus
، وكلتاهما تتألفان من الواجهتين PrintingFishAction
وFishColor
، ولكن يتم تفويض التنفيذ إليهما.
تعدّ تفويض الواجهة أداة فعّالة، وعليك بشكل عام التفكير في كيفية استخدامها كلما احتجت إلى استخدام فئة مجرّدة بلغة أخرى. تتيح لك استخدام التركيب لتضمين السلوكيات، بدلاً من الحاجة إلى العديد من الفئات الفرعية، كل منها متخصص بطريقة مختلفة.
تشبه فئة البيانات struct
في بعض اللغات الأخرى، فهي موجودة بشكل أساسي للاحتفاظ ببعض البيانات، ولكنّ عنصر فئة البيانات يظلّ عنصرًا. تتضمّن عناصر فئة بيانات Kotlin بعض المزايا الإضافية، مثل الأدوات المساعدة للطباعة والنسخ. في هذه المهمة، ستنشئ فئة بيانات بسيطة وتتعرّف على الميزات التي توفّرها Kotlin لفئات البيانات.
الخطوة 1: إنشاء فئة بيانات
- أضِف حزمة جديدة
decor
ضمن الحزمة example.myapp لتضمين الرمز الجديد. انقر بزر الماوس الأيمن على example.myapp في نافذة المشروع واختَر ملف > جديد > حزمة. - في الحزمة، أنشئ فئة جديدة باسم
Decoration
.
package example.myapp.decor
class Decoration {
}
- لتحويل
Decoration
إلى فئة بيانات، أضِف البادئةdata
إلى تعريف الفئة. - أضِف السمة
String
التي تحمل الاسمrocks
لمنح الفئة بعض البيانات.
data class Decoration(val rocks: String) {
}
- في الملف، خارج الفئة، أضِف الدالة
makeDecorations()
لإنشاء وطباعة مثيل منDecoration
باستخدام"granite"
.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
}
- أضِف الدالة
main()
لاستدعاءmakeDecorations()
، ثم شغِّل برنامجك. لاحظ الناتج المعقول الذي تم إنشاؤه لأنّ هذا النوع هو فئة بيانات.
⇒ Decoration(rocks=granite)
- في
makeDecorations()
، أنشئ عنصرَين آخرَين من النوعDecoration
يكون كلاهما "slate" واطبعهما.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
val decoration2 = Decoration("slate")
println(decoration2)
val decoration3 = Decoration("slate")
println(decoration3)
}
- في
makeDecorations()
، أضِف عبارة طباعة تعرض نتيجة مقارنةdecoration1
بـdecoration2
، وعبارة ثانية تقارنdecoration3
بـdecoration2
. استخدِم طريقة equals() التي توفّرها فئات البيانات.
println (decoration1.equals(decoration2))
println (decoration3.equals(decoration2))
- تشغيل الرمز
⇒ Decoration(rocks=granite) Decoration(rocks=slate) Decoration(rocks=slate) false true
الخطوة 2: استخدام تفكيك البنية
للوصول إلى خصائص عنصر بيانات وتعيينها إلى متغيرات، يمكنك تعيينها واحدة تلو الأخرى، على النحو التالي.
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، بما في ذلك ما يلي:
- فئات Singleton
- عمليات التعداد
- الفئات المحكمة
الخطوة 1: تذكُّر فئات العناصر الفردية
تذكَّر المثال السابق الذي يتضمّن الفئة GoldColor
.
object GoldColor : FishColor {
override val color = "gold"
}
بما أنّ كل مثيل من GoldColor
ينفّذ الإجراء نفسه، يتم تعريفه على أنّه object
بدلاً من class
لجعله كائنًا فرديًا. يمكن أن يكون هناك مثيل واحد فقط.
الخطوة 2: إنشاء تعداد
تتيح لك لغة Kotlin أيضًا استخدام التعدادات التي تسمح لك بتعداد عنصر ما والإشارة إليه بالاسم، تمامًا كما هو الحال في اللغات الأخرى. يمكنك تعريف تعداد باستخدام الكلمة الرئيسية enum
قبل التعريف. لا يحتاج تعريف التعداد الأساسي إلا إلى قائمة بالأسماء، ولكن يمكنك أيضًا تحديد حقل واحد أو أكثر مرتبط بكل اسم.
- في ملف Decoration.kt، جرِّب مثالاً على تعداد.
enum class Color(val rgb: Int) {
RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}
تشبه التعدادات إلى حد ما الكائنات الفردية، إذ لا يمكن أن يكون هناك سوى واحد، وواحد فقط من كل قيمة في التعداد. على سبيل المثال، يمكن أن يكون هناك Color.RED
واحد فقط وColor.GREEN
واحد وColor.BLUE
واحد. في هذا المثال، يتم تعيين قيم الأحمر والأخضر والأزرق إلى السمة rgb
لتمثيل مكوّنات اللون. يمكنك أيضًا الحصول على القيمة الترتيبية لتعداد باستخدام السمة ordinal
، واسمه باستخدام السمة name
.
- جرِّب مثالاً آخر على تعداد.
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 جميع الفئات الفرعية بشكل ثابت. وهذا يعني أنّه في وقت الترجمة البرمجية، يرى المترجم البرمجي جميع الفئات والفئات الفرعية ويعرف أنّ هذه هي جميعها، لذا يمكن للمترجم البرمجي إجراء عمليات تحقّق إضافية نيابةً عنك.
- في ملف AquariumFish.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 باستخدام
class
. - تنشئ Kotlin تلقائيًا دوال setter وgetter للخصائص.
- يمكنك تحديد الدالة الإنشائية الأساسية مباشرةً في تعريف الفئة. على سبيل المثال:
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.
- الفئات والوراثة
- الدوال الإنشائية
- الدوال الأصلية
- السمات والحقول
- معدِّلات مستوى الظهور
- الفئات المجردة
- الواجهات
- التفويض
- فئات البيانات
- Equality
- تفكيك البنية
- تعريفات العناصر
- فئات التعداد
- الفئات المحكمة
- التعامل مع الأخطاء الاختيارية باستخدام فئات Kotlin المحكمة
برامج تعليمية حول Kotlin
يتضمّن الموقع الإلكتروني https://try.kotlinlang.org برامج تعليمية غنية بصريًا تُعرف باسم Kotlin Koans، ومترجم مستند إلى الويب، ومجموعة كاملة من المستندات المرجعية مع أمثلة.
دورة Udacity التدريبية
للاطّلاع على دورة Udacity التدريبية حول هذا الموضوع، يُرجى الانتقال إلى برنامج Kotlin التدريبي للمبرمجين.
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
لمنح حيوان مياه نظيفة
▢ A data
class for an entry in a feeding schedule
الانتقال إلى الدرس التالي:
للحصول على نظرة عامة على الدورة التدريبية، بما في ذلك روابط تؤدي إلى دروس تطبيقية أخرى حول الترميز، يُرجى الاطّلاع على "برنامج Kotlin التدريبي للمبرمجين: مرحبًا بك في الدورة التدريبية".