Menu
CoddyTech

初始化器与 super

本课属于 Coddy Swift 学习旅程的“面向对象编程”单元。第 24 课,共 57 课。

当子类添加没有默认值的存储属性时,它需要自己的初始化器。它会先设置自己的属性,然后使用 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)")

输出:

Pip false

顺序很重要:子类属性必须先有值,super.init 才能运行;继承的属性只能在它运行后使用。Swift 会在编译时检查这一点:

init(name: String, canFly: Bool) {
    // print(self.name)      // 错误:在 super.init 之前使用了 self
    self.canFly = canFly     // 1. 自身的属性
    super.init(name: name)   // 2. 父类
    print(self.name)         // 3. 现在可以使用所有内容了
}

子类定义了自己的初始化器后,就不再继承超类的初始化器。子类也可以使用相同的参数重写初始化器,并将其标记为 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)

输出:

0
30

层级中的每一层都会初始化自己的部分,并将剩余部分交给它的超类,因此继承链中的每个属性都会且仅会获得一次值:

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

输出:

City Cabs: 4 wheels, 5 seats
challenge icon

挑战

简单

Product 用于存储 name 和 price(单位为美分)。声明子类 DiscountedProduct,其中包含存储属性 percent。它的初始化器接收 name、price 和 percent,存储 percent,并使用已按百分比折扣后的价格(向下取整)调用 super.init。超出 0-100 范围的 percent 按 0 处理。在 DiscountedProduct 中添加方法 label(),返回 tea: 450 (10% off)。

提供的代码会读取格式为 name,price,percent 的行,为每行创建一个 DiscountedProduct,并打印 label()。

你的代码写在 Product.swift 和 DiscountedProduct.swift 中。main.swift 包含提供的输入/输出代码,不能编辑。

亲手试试

// 提供的输入/输出代码:保持原样
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 icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Swift 在线编辑器