Menu
CoddyTech

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

جزء من قسم البرمجة كائنية التوجه في رحلة 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)   // خطأ: هذا النوع final، لذا لا يمكن الوراثة منه

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

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

open class Account(val owner: String)
class SavingsAccount(owner: String) : Account(owner)   // هو نوع من: الوراثة

class Engine(val horsepower: Int)
class Car(val engine: Engine)                          // يملك: خاصية
challenge icon

التحدي

سهل

يكرر Manager ما لدى Employee بالفعل. اجعل Employee مفتوحًا وحوّل Manager إلى فئته الفرعية: تمرر Manager(name: String, salary: Int) القيمتين إلى Employee وتحتفظ فقط بما هو جديد، أي قائمة خاصة بأسماء التقارير مع addReport(name) وteamSize(). في Employee، تزيد raise(percent) الراتب بتلك النسبة، مع التقريب إلى الأسفل، وتُرجع 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