Menu
CoddyTech

super và hàm khởi tạo

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 19 trên 57.

super.method() gọi phiên bản của phương thức trong lớp cha từ bên trong một phương thức ghi đè. Lớp con có thể mở rộng hành vi được kế thừa thay vì thay thế nó:

open class Notification(val text: String) {
    open fun render() = "[info] $text"
}
class Reminder(text: String, val minutes: Int) : Notification(text) {
    override fun render() = super.render() + " (in $minutes min)"
}

Bên trong main:

println(Notification("backup done").render())
println(Reminder("meeting", 10).render())

Đầu ra:

[info] backup done
[info] meeting (in 10 min)

Mỗi lớp con phải gọi hàm khởi tạo của lớp cha. Với hàm khởi tạo chính, lệnh gọi nằm trong phần khai báo lớp, : Base(arguments), và các đối số có thể được tính từ các tham số của lớp con:

open class Product(val name: String, val price: Int)

class Discounted(name: String, price: Int, val percent: Int) :
    Product(name, price * (100 - percent) / 100)

Bên trong main:

val tea = Discounted("tea", 500, 10)
println("${tea.name} ${tea.price} (${tea.percent}% off)")

Đầu ra:

tea 450 (10% off)

Quá trình khởi tạo diễn ra từ trên xuống dưới theo thứ bậc: các hàm khởi tạo thuộc tính và khối init của lớp cha chạy trước, sau đó đến lớp con:

open class Base(name: String) {
    init { println("Base init for $name") }
}
class Child(name: String) : Base(name.uppercase()) {
    init { println("Child init for $name") }
}

Bên trong main:

Child("kit")

Đầu ra:

Base init for KIT
Child init for kit

Một lớp không có hàm khởi tạo chính sẽ gọi lớp cha từ từng hàm khởi tạo phụ bằng super(...), hoặc ủy quyền cho một hàm khởi tạo khác của chính nó bằng this(...):

open class Shape(val name: String)

class Rect : Shape {
    val w: Int
    val h: Int

    constructor(w: Int, h: Int) : super("rect") {
        this.w = w
        this.h = h
    }

    constructor(side: Int) : this(side, side)
}

Bên trong main:

val r = Rect(3)
println("${r.name} ${r.w}x${r.h}")

Đầu ra:

rect 3x3

Vì lớp cha được thiết lập trước, nó không nên gọi các thành viên open từ hàm khởi tạo hoặc init. Phương thức ghi đè chạy trước khi các thuộc tính riêng của lớp con có giá trị:

open class Base {
    init { println("Base sees: ${describe()}") }
    open fun describe() = "base"
}
class Child : Base() {
    val label = "child"
    override fun describe() = "label is $label"
}

Bên trong main:

Child()

Đầu ra:

Base sees: label is null
challenge icon

Thử thách

Dễ

Notification(text) có một phương thức mở rộng render() trả về [info] text. Hoàn thiện các lớp con: render() của Warning trả về [warn] TEXT với văn bản được viết hoa, còn Reminder, lớp này cũng lưu minutes, trả về kết quả của lớp cha theo sau là (in 10 min), bằng cách dùng super.

Code được cung cấp đọc các dòng info,text, warn,text hoặc remind,text,10, tạo đối tượng tương ứng trong một List<Notification> và in ra render() cho từng đối tượng.

Đặt code của bạn trong Notification.kt, Warning.kt và Reminder.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ó sẵn: giữ nguyên
    val input = generateSequence(::readLine).toList()
    val notes = mutableListOf<Notification>()
    for (line in input) {
        val p = line.split(",")
        notes.add(when (p[0]) {
            "warn" -> Warning(p[1])
            "remind" -> Reminder(p[1], p[2].toInt())
            else -> Notification(p[1])
        })
    }
    for (n in notes) println(n.render())
}
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