Menu
CoddyTech

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

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

Khi lớp con thêm một thuộc tính được lưu trữ mà không có giá trị mặc định, lớp đó cần có hàm khởi tạo riêng. Trước tiên, lớp đặt các thuộc tính của mình, sau đó gọi hàm khởi tạo của lớp cha bằng super.init(...):

class Animal {
    let name: String
    init(name: String) { self.name = name }
}

class Bird: Animal {
    let canFly: Bool

    init(name: String, canFly: Bool) {
        self.canFly = canFly
        super.init(name: name)
    }
}

let p = Bird(name: "Pip", canFly: false)
print("\(p.name) \(p.canFly)")

Đầu ra:

Pip false

Thứ tự rất quan trọng: các thuộc tính của lớp con phải có giá trị trước khi super.init chạy, còn các thuộc tính được kế thừa chỉ có thể dùng sau đó. Swift kiểm tra điều này khi biên dịch:

init(name: String, canFly: Bool) {
    // print(self.name)      // lỗi: dùng self trước super.init
    self.canFly = canFly     // 1. các thuộc tính của chính lớp này
    super.init(name: name)   // 2. lớp cha
    print(self.name)         // 3. giờ có thể dùng mọi thứ
}

Khi lớp con tự định nghĩa hàm khởi tạo, nó không còn kế thừa các hàm khởi tạo của lớp cha. Lớp con cũng có thể ghi đè hàm khởi tạo với cùng các tham số, bằng cách đánh dấu là override init:

class Timer {
    var seconds: Int
    init(seconds: Int) { self.seconds = seconds }
}

class SafeTimer: Timer {
    override init(seconds: Int) {
        super.init(seconds: max(0, seconds))
    }
}

print(SafeTimer(seconds: -5).seconds)
print(SafeTimer(seconds: 30).seconds)

Đầu ra:

0
30

Mỗi cấp trong hệ thống phân cấp khởi tạo phần riêng của mình rồi chuyển phần còn lại cho lớp cha, nhờ đó mỗi thuộc tính trong chuỗi đều nhận giá trị đúng một lần:

class Vehicle {
    let wheels: Int
    init(wheels: Int) { self.wheels = wheels }
}

class Car: Vehicle {
    let seats: Int
    init(seats: Int) {
        self.seats = seats
        super.init(wheels: 4)
    }
}

class Taxi: Car {
    let company: String
    init(company: String) {
        self.company = company
        super.init(seats: 5)
    }
}

let t = Taxi(company: "City Cabs")
print("\(t.company): \(t.wheels) wheels, \(t.seats) seats")

Đầu ra:

City Cabs: 4 wheels, 5 seats
challenge icon

Thử thách

Dễ

Product lưu name và price (tính bằng cent). Khai báo lớp con DiscountedProduct với thuộc tính lưu trữ percent. Hàm khởi tạo nhận name, price và percent, lưu percent rồi gọi super.init với giá đã giảm theo phần trăm (làm tròn xuống). Giá trị percent nằm ngoài khoảng từ 0 đến 100 được tính là 0. Thêm phương thức label() vào DiscountedProduct, phương thức này trả về tea: 450 (10% off).

Code được cung cấp đọc các dòng theo định dạng name,price,percent, tạo một DiscountedProduct cho mỗi dòng và in ra label().

Đặt code của bạn trong Product.swift và DiscountedProduct.swift. main.swift 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ử

// Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    let p = line.split(separator: ",").map { String($0) }
    let item = DiscountedProduct(name: p[0], price: Int(p[1])!, percent: Int(p[2])!)
    print(item.label())
}
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 Swift