Menu
Coddy logo textTech

Инициализаторы и super

Часть раздела Объектно-ориентированное программирование путешествия по Swift на Coddy. Урок 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)      // ошибка: self использован до super.init
    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, передавая цену, уже уменьшенную на указанный процент (с округлением вниз). Значение percent вне диапазона 0–100 считается равным 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