Наследование
Подклассы для добавления или переопределения функциональности.
Класс может унаследовать методы, свойства и другие характеристики от другого класса. Когда один класс наследуется от другого, наследуемый класс известен как подкласс, а класс, от которого он наследуется, известен как его суперкласс. Наследование — это фундаментальное поведение, которое отличает классы от других типов в 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внутри реализации переопределяющего геттера или сеттера. - Переопределённый индексатор для
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 Переопределение наблюдателей свойств
Вы можете использовать переопределение свойств для добавления наблюдателей свойств к унаследованному свойству. Это позволяет получать уведомления об изменениях значения унаследованного свойства, независимо от того, как это свойство было первоначально реализовано. Дополнительную информацию о наблюдателях свойств см. в <doc:Свойства#Наблюдатели-свойств>.
Примечание: Вы не можете добавить наблюдатели свойств к унаследованным неизменяемым хранимым свойствам или унаследованным вычисляемым свойствам только для чтения. Значение этих свойств нельзя установить, поэтому нецелесообразно предоставлять реализацию
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 метод, свойство или индексацию в подклассе сообщается как ошибка времени компиляции. Методы, свойства или индексации, которые вы добавляете в класс в расширении, также могут быть помечены как final в определении расширения. Для получения дополнительной информации см. Расширения.
Вы можете пометить весь класс как final, написав модификатор final перед ключевым словом class в его определении класса (final class). Любая попытка создания подкласса final класса сообщается как ошибка времени компиляции.
This source file is part of the Swift.org open source project
Copyright © 2014 - 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception