Menu
CoddyTech

Ghi đè thành viên

Thuộc phần Lập trình hướng đối tượng trong hành trình Kotlin của Coddy. Bài học 18 trên 57.

Lớp con có thể thay thế một phương thức được kế thừa bằng phiên bản riêng. Lớp cha đánh dấu phương thức là open, còn lớp con khai báo lại phương thức đó với 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"
}

Bên trong main:

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

Đầu ra:

Woof
Meow
...

Phiên bản được chạy do lớp thực tế của đối tượng quyết định, ngay cả khi kiểu của biến là lớp cha. Code bên trong lớp cha gọi một phương thức có thể ghi đè cũng nhận được phiên bản của lớp con:

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

Bên trong main:

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

Đầu ra:

Rex says Woof
Generic says ...

Các thuộc tính có thể được ghi đè theo cách tương tự: dùng open val trong lớp cha, override val trong lớp con, dưới dạng thuộc tính thông thường, thuộc tính trong hàm khởi tạo hoặc getter được tính toán:

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"
}

Bên trong main:

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

Đầu ra:

shape: 0
triangle: 3
6-gon: 6

Thành viên được đánh dấu override lại có thể được ghi đè, vì vậy lớp con của lớp con có thể ghi đè thành viên đó thêm một lần nữa; final override sẽ ngăn điều đó. Bạn hoàn toàn không thể ghi đè thành viên không có open, và lớp con không thể khai báo thành viên có cùng chữ ký:

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"   // lỗi: 'sound' trong 'Dog' là final
    // fun sleep() = "nap"            // lỗi: 'sleep' che khuất một thành viên của lớp cha
}
challenge icon

Thử thách

Dễ

Animal có legs() (4), sound() (...) và describe(), hàm này trả về Rex has 4 legs and says Woof, được tạo từ legs() và sound(). Hãy cho phép ghi đè legs() và sound(), rồi hoàn thiện các lớp con bằng cách chỉ ghi đè hai phương thức đó: Dog kêu Woof, Bird có 2 chân và kêu Tweet, còn Snake có 0 chân và kêu Hiss. describe() vẫn giữ nguyên như trong Animal.

Code được cung cấp đọc các dòng dog,Rex, bird,Pip, snake,Kaa hoặc bất kỳ loại nào khác (một Animal thông thường), lưu các con vật vào một List<Animal> và in ra describe() cho từng con.

Code của bạn nằm trong Animal.kt, Dog.kt, Bird.kt và Snake.kt. Main.kt chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

fun main() {
    // Đoạn code đầu vào/đầu ra đã được cung cấp: giữ nguyên
    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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Playground Kotlin