דריסת איברים
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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' מסתירה איבר של מחלקת האב
}אתגר
קלל-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())
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1מחלקות ואובייקטים
עבודה עם קבציםהגדרת מחלקותבנאים ראשייםבלוקי init ואימות נתוניםבנאים משנייםסיכום – כרטיס ספרייהתרגלו בעצמכם: Kotlin אונליין