Наследование
Класс может наследовать методы, свойства и другие характеристики от другого класса. Когда один класс наследовался от другого, наследующий класс известен как подкласс, и класс, от которого это наследовалось, известен как его суперкласс. Наследование является фундаментальным поведением, дифференцирующим классы от других типов в Swift.
Классы в Swift могут вызвать и методы доступа, свойства и нижние индексы, принадлежащие их суперклассу, и могут обеспечить свои собственные переопределяющие версии тех методов, свойств и нижних индексов, чтобы совершенствовать или изменить их поведение. Swift помогает гарантировать, что Ваши переопределения корректны путем проверки, что определение переопределения имеет соответствующее суперопределение класса.
Классы могут также добавить наблюдателей свойства к унаследованным свойствам, чтобы быть уведомленными, когда изменяется значение свойства. Наблюдатели свойства могут быть добавлены к любому свойству, независимо от того, было ли оно первоначально определено как сохраненное или вычисленное свойство.
Определение базового класса
Любой класс, не наследовавшийся от другого класса, известен как базовый класс.
Пример ниже определяет вызванный базовый класс Vehicle
. Этот базовый класс определяет сохраненное вызванное свойство currentSpeed
, со значением по умолчанию 0.0
(выведение типа свойства Double
). currentSpeed
значение свойства используется вычисленным только для чтения String
свойство вызывают description
создать описание механизма.
Vehicle
базовый класс также определяет вызванный метод makeNoise
. Этот метод ничего фактически не делает для основы Vehicle
экземпляр, но будет настроен подклассами Vehicle
позже:
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
Вы создаете новый экземпляр Vehicle
с синтаксисом инициализатора, записанным как a TypeName
сопровождаемый пустыми круглыми скобками:
let someVehicle = Vehicle()
Создав новое Vehicle
экземпляр, можно получить доступ к description
свойство для печати человекочитаемого описания текущей скорости механизма:
println("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour
Vehicle
класс определяет общие характеристики для произвольного механизма, но не полезен сам по себе. Для создания его более полезным необходимо совершенствовать его для описания более определенных видов механизма.
Разделение на подклассы
Разделение на подклассы является действием базирования нового класса на существующем классе. Подкласс наследовал характеристики от существующего класса, который можно тогда совершенствовать. Можно также добавить новые характеристики к подклассу.
Чтобы указать, что подкласс имеет суперкласс, напишите имя подкласса перед суперименем класса, разделенным двоеточием:
class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
Следующий пример определяет вызванный подкласс Bicycle
, с суперклассом Vehicle
:
class Bicycle: Vehicle {
var hasBasket = false
}
Новое Bicycle
класс автоматически получает все характеристики Vehicle
, такой как currentSpeed
и description
свойства и makeNoise()
метод.
В дополнение к характеристикам это наследовалось, Bicycle
класс определяет новое сохраненное свойство, hasBasket
, со значением по умолчанию false
(выведение типа Bool
для свойства).
По умолчанию, любой новый Bicycle
экземпляр, который Вы создаете, не будет иметь корзины. Можно установить hasBasket
свойство к true
для детали Bicycle
экземпляр после того экземпляра создается:
let bicycle = Bicycle()
bicycle.hasBasket = true
Можно также изменить наследованный currentSpeed
свойство a Bicycle
экземпляр и запрос экземпляр наследовались description
свойство:
bicycle.currentSpeed = 15.0
println("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
Подклассы могут самостоятельно быть разделены на подклассы. Следующий пример создает подкласс Bicycle
для двухместного велосипеда, известного как «тандем»:
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
Tandem
наследовал все свойства и методы от Bicycle
, который поочередно наследовал все свойства и методы от Vehicle
. Tandem
подкласс также добавляет новое сохраненное вызванное свойство currentNumberOfPassengers
, со значением по умолчанию 0
.
Если Вы создаете экземпляр Tandem
, можно работать с любым из его новых и унаследованных свойств и запросить только для чтения description
свойство это наследовалось от Vehicle
:
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
println("Tandem: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour
Переопределение
Подкласс может обеспечить свою собственную реализацию метода экземпляра, ввести метод, свойство экземпляра, ввести свойство или нижний индекс, который это иначе наследовало бы от суперкласса. Это известно как переопределение.
Для переопределения характеристики, которая была бы иначе наследована Вы снабжаете префиксом свое переопределяющее определение override
ключевое слово. Выполнение так разъясняет, что Вы намереваетесь обеспечить переопределение и не обеспечили соответствующее определение по ошибке. Переопределение случайно может вызвать неожиданное поведение и любые переопределения без override
когда Ваш код компилируется, ключевое слово диагностировано как ошибка.
override
ключевое слово также запрашивает компилятор Swift проверять, что суперкласс Вашего переопределяющего класса (или один из его родителей) имеет объявление, соответствующее тот, который Вы предусмотрели переопределение. Эта проверка гарантирует, что Ваше переопределяющее определение корректно.
Получая доступ к методам суперкласса, свойствам и нижним индексам
Когда Вы обеспечиваете метод, свойство, или преобразовываете переопределение в нижний индекс для подкласса, иногда полезно использовать существующую реализацию суперкласса в качестве части Вашего переопределения. Например, можно совершенствовать поведение той существующей реализации или сохранить измененное значение в существующей наследованной переменной.
Где это является надлежащим, Вы получаете доступ к версии суперкласса метода, свойства или нижнего индекса при помощи super
префикс:
Переопределенный метод называют
someMethod()
может вызвать версию суперклассаsomeMethod()
путем вызоваsuper.someMethod()
в реализации метода переопределения.Переопределенное свойство вызывают
someProperty
может получить доступ к версии суперклассаsomeProperty
какsuper.someProperty
в переопределяющем методе get или реализации метода set.Переопределенный нижний индекс для
someIndex
может получить доступ к версии суперкласса того же нижнего индекса какsuper[someIndex]
из переопределяющей нижней реализации.
Методы переопределения
Можно переопределить наследованный экземпляр или ввести метод для обеспечения адаптированной или альтернативной реализации метода в подклассе.
Следующий пример определяет новый подкласс Vehicle
вызванный Train
, который переопределяет makeNoise()
метод это Train
наследовался от Vehicle
:
class Train: Vehicle {
override func makeNoise() {
println("Choo Choo")
}
}
Если Вы создаете новый экземпляр Train
и вызовите makeNoise()
метод, Вы видите что Train
версию подкласса метода вызывают:
let train = Train()
train.makeNoise()
// prints "Choo Choo"
Переопределение свойств
Когда базовое значение свойства изменяется, можно переопределить наследованный экземпляр или свойство класса, чтобы предоставить собственному методу get и методу set для того свойства, или добавить наблюдателей свойства, чтобы позволить переопределяющему свойству наблюдать.
Переопределение методов get свойства и методов set
Можно предоставить пользовательскому методу get (и метод set, если надлежащий) для переопределения любого унаследованного свойства, независимо от того, реализовано ли унаследованное свойство как сохраненное или вычисленное свойство в источнике. Сохраненная или вычисленная природа унаследованного свойства не известна подклассом — она только знает, что унаследованное свойство имеет определенное имя и тип. Необходимо всегда утверждать и имя и тип свойства, Вы являетесь переопределяющими, чтобы позволить компилятору проверить, что Ваше переопределение соответствует свойство суперкласса тому же имени и типу.
Можно представить наследованное свойство только для чтения как свойство чтения/записи путем обеспечения и метода get и метода set в переопределении свойства подкласса. Вы не можете, однако, представить наследованное свойство чтения/записи как свойство только для чтения.
Следующий пример определяет новый вызванный класс Car
, который является подклассом Vehicle
. Car
класс представляет новое сохраненное вызванное свойство gear
, с целочисленным значением по умолчанию 1
. Car
класс также переопределяет description
свойство это наследовалось от Vehicle
, предоставлять пользовательское описание, включающее текущее имущество:
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
Переопределение description
свойство запускается путем вызова super.description
, который возвращается Vehicle
класс description
свойство. Car
версия класса description
тогда добавляет некоторый дополнительный текст на конец этого описания для предоставления информации о текущем имуществе.
Если Вы создаете экземпляр Car
класс и набор gear
и currentSpeed
свойства, Вы видите что description
свойство возвращает адаптированное описание, определенное в Car
класс:
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
println("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3
Переопределение наблюдателей свойства
Можно использовать переопределение свойства для добавления наблюдателей свойства к унаследованному свойству. Это позволяет Вам быть уведомленными, когда значение унаследованного свойства изменяется, независимо от того, как было первоначально реализовано то свойство. Для получения дополнительной информации о наблюдателях свойства посмотрите Наблюдателей Свойства.
Следующий пример определяет новый вызванный класс AutomaticCar
, который является подклассом Car
. AutomaticCar
класс представляет автомобиль с автоматической коробкой передач, автоматически выбирающей надлежащее имущество для использования на основе текущей скорости:
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
Каждый раз, когда Вы устанавливаете currentSpeed
свойство AutomaticCar
экземпляр, свойство didSet
наблюдатель устанавливает экземпляр gear
свойство к надлежащему выбору имущества для новой скорости. В частности наблюдатель свойства выбирает имущество, которое является новым currentSpeed
значение, разделенное на 10
, округленный в меньшую сторону до самого близкого целого числа, плюс 1
. Скорость 35.0
производит имущество 4
:
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
println("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
Предотвращение переопределений
Можно предотвратить метод, свойство, или преобразовать в нижний индекс от того, чтобы быть переопределенным путем маркировки его как финал. Сделайте это путем записи final
модификатор перед методом, свойством или introducer ключевым словом нижнего индекса (такой как final var
, final func
, final class func
, и final subscript
).
О любой попытке переопределить последний метод, свойство или нижний индекс в подклассе сообщают как ошибка времени компиляции. Методы, свойства или нижние индексы, которые Вы добавляете к классу в расширении, могут также быть отмечены как финал в определении расширения.
Можно отметить весь класс как финал путем записи final
модификатор перед class
ключевое слово в его определении класса (final class
). О любой попытке разделить заключительный класс на подклассы сообщают как ошибка времени компиляции.