Класс может наследовать методы, свойства и другие характеристики от другого класса. Когда один класс наследует от другого, наследующий класс называется подклассом , а класс, от которого он наследуется, называется его суперклассом . Наследование - это фундаментальное поведение, которое отличает классы от других типов в Swift.
Классы в 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
с синтаксисом инициализатора , который записывается как имя типа, за которым следуют пустые скобки:
let someVehicle = Vehicle()
Создав новый Vehicle
экземпляр, вы можете получить доступ к его description
свойству, чтобы распечатать понятное человеку описание текущей скорости автомобиля:
print("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
свойство Bicycle
экземпляра и запросить унаследованное description
свойство экземпляра :
bicycle.currentSpeed = 15.0
print("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
print("Tandem: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour
Переопределение
Подкласс может предоставить свою собственную пользовательскую реализацию метода экземпляра, метода типа, свойства экземпляра, свойства типа или индекса, который он иначе унаследовал бы от суперкласса. Это известно как переопределение .
Чтобы переопределить характеристику, которая в противном случае была бы унаследована, вы добавляете к префиксному определению override
ключевое слово. Это разъясняет, что вы намереваетесь предоставить переопределение и не предоставили соответствующее определение по ошибке. Переопределение случайно может вызвать непредвиденное поведение, и любые переопределения без override
ключевого слова диагностируются как ошибка при компиляции кода.
override
Ключевое слово также побуждает Swift компилятор , чтобы проверить , что суперкласс вашего Переопределение класса (или один из его родителей) имеет декларацию , которая соответствует одному вы предоставили для переопределения. Эта проверка гарантирует, что ваше главное определение верно.
Доступ к методам, свойствам и подпискам суперкласса
Когда вы предоставляете метод, свойство или переопределение индекса для подкласса, иногда полезно использовать существующую реализацию суперкласса как часть вашего переопределения. Например, вы можете уточнить поведение этой существующей реализации или сохранить измененное значение в существующей унаследованной переменной.
Где это уместно, вы получаете доступ к суперклассовой версии метода, свойства или индекса, используя super
префикс:
- Переопределенный именованный метод
someMethod()
может вызывать версию суперклассаsomeMethod()
путем вызоваsuper.someMethod()
внутри реализации переопределенного метода. - Перезаписанное свойство называется
someProperty
может получить доступ к версии суперклассаsomeProperty
как вsuper.someProperty
рамках реализации переопределения getter или setter. - Переопределенный индекс для
someIndex
доступа может получить доступ к версии суперкласса того же индекса, что иsuper[someIndex]
из реализации переопределенного индекса.
Переопределяющие методы
Вы можете переопределить унаследованный экземпляр или метод типа, чтобы обеспечить индивидуальную или альтернативную реализацию метода в вашем подклассе.
Следующий пример определяет новый подкласс Vehicle
под названием Train
, который переопределяет makeNoise()
метод , который Train
наследует от Vehicle
:
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
Если вы создадите новый экземпляр класса Train
и вызовете его makeNoise()
метод, вы увидите, что Train
вызывается версия метода подкласса:
let train = Train()
train.makeNoise()
// Prints "Choo Choo"
Переопределение свойств
Вы можете переопределить унаследованное свойство экземпляра или типа, чтобы предоставить свой собственный метод получения и установки для этого свойства, или добавить наблюдатели свойства, чтобы свойство переопределения могло наблюдать, когда изменяется значение базового свойства.
Переопределение методов получения и установки свойств
Вы можете предоставить собственный метод получения (и, при необходимости, установку) для переопределения любого унаследованного свойства, независимо от того, реализовано ли унаследованное свойство как хранимое или вычисляемое свойство в источнике. Хранимая или вычисленная природа унаследованного свойства не известна подклассу - он только знает, что унаследованное свойство имеет определенное имя и тип. Вы всегда должны указывать как имя, так и тип свойства, которое вы переопределяете, чтобы компилятор мог проверить, соответствует ли ваше переопределение свойству суперкласса с тем же именем и типом.
Вы можете представить унаследованное свойство только для чтения как свойство для чтения и записи, указав в переопределении свойства подкласса как метод получения, так и метод установки. Однако вы не можете представить унаследованное свойство чтения-записи как свойство только для чтения.
ЗАМЕТКА
Если вы предоставляете установщик как часть переопределения свойства, вы также должны предоставить получатель для этого переопределения. Если вы не хотите изменять значение унаследованного свойства в переопределяющем получателе, вы можете просто пройти через унаследованное значение, вернувшись
super.someProperty
из получателя, гдеsomeProperty
находится имя переопределяемого свойства.
В следующем примере определяется новый класс с именем 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
print("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3
Переопределение наблюдателей за недвижимостью
Вы можете использовать переопределение свойств для добавления наблюдателей свойств в унаследованное свойство. Это позволяет получать уведомления об изменении значения унаследованного свойства независимо от того, как это свойство было изначально реализовано.
ЗАМЕТКА
Вы не можете добавлять наблюдатели свойств в унаследованные постоянные сохраненные свойства или унаследованные вычисляемые свойства только для чтения. Значение этих свойств не может быть установлено, и поэтому нецелесообразно предоставлять
willSet
илиdidSet
реализацию как часть переопределения.
Также обратите внимание, что вы не можете предоставить как переопределитель сеттера, так и наблюдатель переопределения свойства для одного свойства. Если вы хотите наблюдать изменения в значении свойства, и вы уже предоставляете пользовательский установщик для этого свойства, вы можете просто наблюдать любые изменения значения из пользовательского установщика.
В следующем примере определяется новый класс с именем 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
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
Предотвращение переопределений
Вы можете предотвратить переопределение метода, свойства или индекса, пометив его как final . Делайте это, написав final
модификатор перед тем интродьюсером по ключевому слову метод, свойства или подстрочного (например, если , , и ).final var
final func
final class func
final subscript
Любая попытка переопределить конечный метод, свойство или индекс в подклассе сообщается как ошибка времени компиляции. Методы, свойства или индексы, которые вы добавляете в класс в расширении, также могут быть помечены как окончательные в определении расширения.
Вы можете пометить весь класс как окончательный, написав final
модификатор перед class
ключевым словом в его определении класса ( ). Любая попытка создать подкласс окончательного класса сообщается как ошибка времени компиляции.final class
0 комментариев