Menu
Coddy logo textTech

מחלקות פתוחות ותת-מחלקות

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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)   // 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בחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: Kotlin אונליין