Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الأصناف المفتوحة والأصناف الفرعية

جزء من قسم البرمجة كائنية التوجه في رحلة Kotlin على Coddy. الدرس 17 من 57.

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

open class Animal(val name: String) {
    fun describe() = "$name is an animal"
}

class Dog(name: String) : Animal(name) {
    fun fetch() = "$name fetches the ball"
}

داخل main:

val rex = Dog("Rex")
println(rex.describe())
println(rex.fetch())

الناتج:

Rex is an animal
Rex fetches the ball

توضح class Dog(name: String) : Animal(name) أن Dog ترث من Animal وتمرر name إلى مُنشئ الفئة الفائقة. لا يحتوي المُعامل على val، لأن الخاصية موجودة بالفعل في Animal. لا يمكن تمديد فئة ليست open:

class Plant(val name: String)
// class Tree(name: String) : Plant(name)   // خطأ: هذا النوع نهائي، لذا لا يمكن توسيعه

open class Vehicle(val wheels: Int)
class Bike : Vehicle(2)                     // يمرر قيمة ثابتة
class Truck(val load: Int) : Vehicle(6)     // يضيف خاصيته الخاصة

إنّ Dog هو Animal، لذا يمكن استخدام مثيل من الفئة الفرعية أينما كان متوقَّعًا نوع الفئة الفائقة. يمكن لـ List<Animal> واحدة أن تحتوي على كلاب وقطط وحيوانات عادية، ولكل عنصر أعضاء Animal:

open class Animal(val name: String) {
    fun describe() = "$name is an animal"
}
class Dog(name: String) : Animal(name)
class Cat(name: String, val indoor: Boolean) : Animal(name)

داخل main:

val pets: List<Animal> = listOf(Dog("Rex"), Cat("Tom", true), Animal("Generic"))
for (p in pets) println(p.describe())

الناتج:

Rex is an animal
Tom is an animal
Generic is an animal

تمثّل الوراثة علاقة is-a: فالكلب حيوان، وحساب التوفير حساب. عندما يستخدم صنفٌ صنفًا آخر فقط (فالسيارة لها محرّك)، امنحه خاصيةً من ذلك النوع بدلًا من ذلك. الأصناف المغلقة هي الإعداد الافتراضي في Kotlin، لذا يصرّح الصنف المخصّص للتوسعة بذلك باستخدام open، بينما يحافظ كل صنف آخر على سلوكه بأمان من الأصناف الفرعية.

open class Account(val owner: String)
class SavingsAccount(owner: String) : Account(owner)   // is-a: الوراثة

class Engine(val horsepower: Int)
class Car(val engine: Engine)                          // has-a: خاصية
challenge icon

التحدي

سهل

يكرر Manager ما لدى Employee بالفعل. اجعل Employee مفتوحًا وحوّل Manager إلى فئته الفرعية: يمرر Manager(name: String, salary: Int) القيمتين إلى Employee ويحتفظ فقط بما هو جديد، أي قائمة خاصة بأسماء المرؤوسين مع addReport(name) وteamSize(). في Employee، يزيد raise(percent) الراتب بنسبة النسبة المئوية المحددة، مع التقريب إلى الأسفل، ويعيد describe() القيمة Ada earns 5500.

ينشئ الكود المقدم مديرًا من السطر الأول (name,salary)، ويضيف كل اسم لاحق باعتباره مرؤوسًا، ويمنح زيادة بنسبة 10% ويطبع Ada earns 5500 and leads 2، ثم يتحقق من أن المدير هو Employee.

يجب أن تضع كودك في Employee.kt وManager.kt. يحتوي Main.kt على كود الإدخال/الإخراج المقدم ولا يمكن تعديله.

جرّب بنفسك

fun main() {
    // كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
    val input = generateSequence(::readLine).toList()
    val (name, salary) = input[0].split(",")
    val boss = Manager(name, salary.toInt())
    for (report in input.drop(1)) boss.addReport(report)
    boss.raise(10)
    println("${boss.describe()} and leads ${boss.teamSize()}")
    println("a manager is an employee: ${(boss as Any) is Employee}")
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: Playground لـ Kotlin