Menu
Coddy logo textTech

דריסת איברים

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Kotlin של Coddy. שיעור 18 מתוך 57.

תת־מחלקה יכולה להחליף מתודה שעברה בירושה בגרסה משלה. מחלקת־העל מסמנת את המתודה בתור open, ותת־המחלקה מצהירה עליה שוב עם override:

open class Animal(val name: String) {
    open fun sound() = "..."
}
class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof"
}
class Cat(name: String) : Animal(name) {
    override fun sound() = "Meow"
}

בתוך main:

println(Dog("Rex").sound())
println(Cat("Tom").sound())
println(Animal("Generic").sound())

פלט:

Woof
Meow
...

הגרסה שתופעל נקבעת לפי המחלקה בפועל של האובייקט, גם כאשר סוג המשתנה הוא מחלקת-העל. גם קוד בתוך מחלקת-העל שקורא למתודה פתוחה מקבל את הגרסה של תת-המחלקה:

open class Animal(val name: String) {
    open fun sound() = "..."
    fun speak() = "$name says ${sound()}"
}
class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof"
}

בתוך main:

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

פלט:

Rex says Woof
Generic says ...

אפשר לדרוס מאפיינים באותו אופן: open val במחלקת־העל, override val במחלקת־הבת, בין אם כמאפיין רגיל, כמאפיין של בנאי או כ־getter מחושב:

open class Shape {
    open val name = "shape"
    open val corners = 0
}
class Triangle : Shape() {
    override val name = "triangle"
    override val corners = 3
}
class Polygon(override val corners: Int) : Shape() {
    override val name: String
        get() = "$corners-gon"
}

בתוך main:

for (s in listOf(Shape(), Triangle(), Polygon(6))) println("${s.name}: ${s.corners}")

פלט:

shape: 0
triangle: 3
6-gon: 6

איבר שסומן כ-override שוב פתוח לדריסה, כך שתת-מחלקה של תת-המחלקה יכולה לדרוס אותו פעם נוספת; final override מונע זאת. אי אפשר לדרוס כלל איבר שאינו כולל open, ותת-מחלקה אינה רשאית להצהיר על איבר בעל אותה חתימה:

open class Animal {
    open fun sound() = "..."
    fun sleep() = "zzz"
}
open class Dog : Animal() {
    final override fun sound() = "Woof"
}
class Puppy : Dog() {
    // override fun sound() = "Yip"   // שגיאה: 'sound' ב-'Dog' היא final
    // fun sleep() = "nap"            // שגיאה: 'sleep' מסתירה איבר של מחלקת האב
}
challenge icon

אתגר

קל

ל-Animal יש legs() (4), sound() (...) ו-describe(), שמחזירה את Rex has 4 legs and says Woof, שנבנה מתוך legs() ו-sound(). הפוך את legs() ואת sound() לניתנות לדריסה והשלם את מחלקות המשנה באמצעות דריסה של שתי המתודות האלה בלבד: Dog אומרת Woof, ל-Bird יש 2 רגליים והיא אומרת Tweet, ול-Snake יש 0 רגליים והיא אומרת Hiss. describe() נשארת כפי שהיא ב-Animal.

הקוד שסופק קורא שורות dog,Rex, bird,Pip, snake,Kaa או כל סוג אחר (Animal רגילה), מאחסן את החיות ב-List<Animal> אחד ומדפיס את describe() של כל אחת מהן.

הקוד שלך יופיע בקבצים Animal.kt, Dog.kt, Bird.kt ו-Snake.kt. Main.kt מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

fun main() {
    // קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
    val input = generateSequence(::readLine).toList()
    val animals = mutableListOf<Animal>()
    for (line in input) {
        val (kind, name) = line.split(",")
        animals.add(when (kind) {
            "dog" -> Dog(name)
            "bird" -> Bird(name)
            "snake" -> Snake(name)
            else -> Animal(name)
        })
    }
    for (a in animals) println(a.describe())
}
quiz iconבחנו את עצמכם

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

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

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