Одним из ключевых механизмов объектно-ориентированного программирования является наследование. В Swift классы могут наследовать функционал от других классов.
Класс-наследник еще называют подклассом, а класс, от которого наследуется функционал, - базовым классом или суперклассом.
Классы в Swift имеют полноценный доступ ко всем методам, свойствам, которые определены в суперклассе. Однако при необходимости подклассы могут переопределять наследуемый функционал суперклассом, например, изменять поведение методов или свойств.
Общий синтаксис наследования классов выглядит следующим образом:
class SubClass: SuperClass{ }
Для примера наследования рассмотрим простейшую ситуацию. Пусть у нас есть класс человека и класс служащего:
class User{ var name: String var surname: String init(name: String, surname: String){ self.name = name self.surname = surname } func getFullInfo() -> String{ return "\(self.name) \(self.surname)" } } class Employee : User{ var company: String init(name: String, surname: String, company: String){ self.company = company super.init(name: name, surname: surname) } }
Здесь класс Emplyee наследуется от класса User. Ведь класс сотрудника по сути будет повторять функционал класса человека, так как каждый сотрудник имеет имя и фамилию. И наследование в данном случае помогает избежать ненужного повторения при определении свойств и методов.
А после создания объекта Employee мы можем через него обращаться к свойствам и методам базового класса User:
var emp: Employee = Employee(name: "Steve", surname: "Jobs", company:"Apple") var emplInfo = emp.getFullInfo() // Steve Jobs emp.name = "Tim" emp.surname = "Cook"
Ключевое слово super позволяет обращаться из подкласса к свойствам и методам базового класса. В выше приведенном примере ключевое слово super используется для обращения к инициализатору базового класса ля передачи ему значений.
Подкласс может перенимать полностью функционал базового класса, а может и переопределять его. Для переопределения используется ключевое слово override.
class User{ var name: String var surname: String init(name: String, surname: String){ self.name = name self.surname = surname } func getFullInfo() -> String{ return "\(self.name) \(self.surname)" } } class Employee : User{ var company: String init(name: String, surname: String, company: String){ self.company = company super.init(name: name, surname: surname) } override func getFullInfo() -> String{ return "\(self.name) \(self.surname) - \(self.company)" } } var emp: Employee = Employee(name: "Steve", surname: "Jobs", company:"Apple") print(emp.getFullInfo()) // Steve Jobs - Apple
В данном случае мы переопределяем метод getFullInfo()
. Теперь кроме имени и фамилии он также возвращает данные о компании, в которой
сотрудник работает. И всегда, когда мы будем вызывать метод getFullInfo()
у объекта Employee, будет срабатывать именно переопределенная версия метода.
Используя ключевое super, мы можем переопределить метод по-другому:
override func getFullInfo() -> String{ return "\(super.getFullInfo) - \(self.company)" }
или
override func getFullInfo() -> String{ return super.getFullInfo() + " - \(self.company)" }
Подобным образом мы можем переопределять свойства:
class User{ var name: String var surname: String init(name: String, surname: String){ self.name = name self.surname = surname } var fullInfo: String{ return "\(self.name) \(self.surname)" } } class Employee : User{ var company: String init(name: String, surname: String, company: String){ self.company = company super.init(name: name, surname: surname) } override var fullInfo: String{ return super.fullInfo + " - \(self.company)" } } var emp: Employee = Employee(name: "Steve", surname: "Jobs", company:"Apple") print(emp.fullInfo) // Steve Jobs - Apple
В данном случае переопределяется свойство fullInfo
.
При переопределении инициализатора необходимо вызвать инициализатор базового класса для инициализации тех свойств, которые определены в базовом классе. Кроме того, если в подклассе есть собственные свойства, их необходимо инициализировать до вызова инициализатора базового класса:
class User{ var name: String var surname: String init(name: String, surname: String){ self.name = name self.surname = surname } var fullInfo: String{ return "\(self.name) \(self.surname)" } } class Employee : User{ var company: String override init(name: String, surname: String){ self.company = "Unknown" super.init(name: "Mr." + name, surname: surname) } init(name: String, surname: String, company: String){ self.company = company super.init(name: name, surname: surname) } override var fullInfo: String{ return super.fullInfo + " - \(self.company)" } } var emp: Employee = Employee(name: "Tim", surname: "Cook") print(emp.fullInfo) // Mr. Tim Cook - Unknown
В предыдущем примере нам было необязательно переопределять инициализатор класса User в классе Employee. Однако с помощью ключевого слова required мы можем отметить этот инициализатор как обязательный для переопределения в подклассах:
class User{ var name: String var surname: String required init(name: String, surname: String){ self.name = name self.surname = surname } } class Employee : User{ var company: String required init(name: String, surname: String){ self.company = "Unknown" super.init(name: "Mr." + name, surname: surname) } init(name: String, surname: String, company: String){ self.company = company super.init(name: name, surname: surname) } }
С помощью ключевого слова final мы можем запретить переопределение свойств, методов, сабскриптов в производном классе:
class User{ var name: String var surname: String init(name: String, surname: String){ self.name = name self.surname = surname } final var fullInfo: String{ return "\(self.name) \(self.surname)" } }
Теперь свойство fullInfo
нельзя будет переопределить в производном классе.
Более того мы можем вообще запретить наследование класса, поставив перед его определением ключевое слово final:
final class User{ //............. }