Видеокурсы по изучению языка программирования Swift. Подробнее

Переопределение

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Подклассы могут проводить свои собственные реализации методов экземпляра, методов класса, свойств экземпляра, свойств класса или индекса, который в противном случае будет наследовать от суперкласса. Это известно как переопределение.

Для переопределения характеристик, которые все равно будут унаследованы, вы приписываете к переписываемому определению ключевое слово override. Делая так, вы показываете свое намерение провести переопределение, и что оно будет сделано не по ошибке. Переписывание по случайности может вызвать непредвиденное поведение, и любое переопределение без ключевого слова override, будет считаться ошибкой при компиляции кода.

Ключевое слово override так же подсказывает компилятору Swift проверить, что вы переопределяете суперкласс класса (или один из его параметров), который содержит то определение, которое вы хотите переопределить. Эта проверка проверяет, что ваше переопределение определения корректно.

Доступ к методам, свойствам, индексам суперкласса

Когда вы проводите переопределение метода, свойства, индекса для подкласса, иногда бывает полезно использовать существующую реализацию суперкласса как часть вашего переопределения. Для примера, вы можете усовершенствовать поведение существующей реализации или сохранить измененное значение в существующей унаследованной переменной.

Там, где это уместно, вы можете получить доступ к методу, свойству, индексу версии суперкласса, если будете использовать префикс super:

  • Переопределенный метод someMethod может вызвать версию суперкласса метода someMethod, написав super.someMethod() внутри переопределения реализации метода.
  • Переопределённое свойство someProperty может получить доступ к свойству версии суперкласса someProperty как super.someProperty внутри переопределения реализации геттера или сеттера.
  • Переопределенный индекс для someIndex может получить доступ к версии суперкласса того же индекса как super[someIndex] изнутри переопределения реализации индекса.

Переопределение методов

Вы можете переопределить унаследованный метод экземпляра или класса, чтобы обеспечить индивидуальную или альтернативную версию реализации метода в подклассе.

Следующий пример определяет новый подкласс Train класса Vehicle, который переопределяет метод makeNoise(), который Train наследует от Vehicle:

class Train: Vehicle {
  override func makeNoise() {
    print("Чу-чу")
  }
}

Если вы создаете новый экземпляр класса Train и вызовите его метод makeNoise(), вы увидите, что версия метода подкласса Train вызывается вот так:

let train = Train()
train.makeNoise()
// Выведет "Чу-чу"

Переопределение свойств

Вы можете переопределить унаследованные свойства класса или экземпляра для установки вашего собственного геттера и сеттера для этого свойства, или добавить наблюдателя свойства для наблюдения за переопределяемым свойством, когда меняется лежащее в основе значение свойства.

Переопределения геттеров и сеттеров свойства

Вы можете предусмотреть пользовательский геттер (и сеттер, если есть в этом необходимость) для переопределения любого унаследованного свойства, не смотря на то как свойство было определено в самом источнике, как свойство хранения или как вычисляемое. Подкласс не знает каким является унаследованное свойство хранимым или вычисляемым, все что он знает, так это имя свойства и его тип. Вы всегда должны констатировать и имя и тип свойства, которое вы переопределяете, для того чтобы компилятор мог проверить соответствие и наличие переопределяемого свойства у суперкласса.

Вы можете представить унаследованное свойство только для чтения, как свойство, которое можно читать и редактировать, прописывая и геттер и сеттер в вашем переопределяемом свойстве подкласса. Однако вы не можете сделать наоборот, то есть сделать свойство редактируемое и читаемое только свойством для чтения.

Заметка

Если вы предоставляете сеттер как часть переопределения свойства, то вы должны предоставить и геттер для этого переопределения. Если вы не хотите изменять значение наследуемого свойства внутри переопределяемого геттера, то вы можете просто передать через наследуемое значение, возвращая super.someProperty от геттера, где someProperty - имя параметра, который вы переопределяете.

Следующий пример определяет класс Car, который является подклассом Vehicle. Класс Car предоставляет новое свойство хранения gear, имеющее значение по умолчанию равное 1. Класс Car так же переопределяет свойство description, которое он унаследовал от Vehicle, для предоставления собственного описания, которое включает в себя текущую передачу:

class Car: Vehicle {
  var gear = 1
  override var description: String {
    return super.description + " на передаче \(gear)"
  }
}

Переопределение свойства description начинается с super.description, который возвращает свойство description класса Vehicle. Версия класса Car свойства description добавляет дополнительный текст в конец описания текущего свойства description.

Если вы создадите экземпляр класса Car и зададите свойства gear, currentSpeed, то вы увидите что его свойство description возвращает новое описание класса Car:

let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Машина: \(car.description)")
// Выведет "Машина: движется на скорости 25.0 миль в час на 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. Если скорость равно 10.0, то передача равна 1, если скорость 35.0, то передача 4:

let automatic = AutomaticCar ()
automatic.currentSpeed = 35.0
print("Машина с автоматом: \(automatic.description)")
//Выведет "Машина с автоматом движется на скорости 35.0 миль в час на 4 передаче"

 

Swift: 
4.0