Наследование
Одним из ключевых механизмов объектно-ориентированного программирования является наследование. В Swift классы могут наследовать функционал от других классов.
Класс-наследник еще называют подклассом, а класс, от которого наследуется функционал, - базовым классом или суперклассом.
Классы в Swift имеют полноценный доступ ко всем методам, свойствам, которые определены в суперклассе. Однако при необходимости подклассы могут переопределять наследуемый функционал суперклассом, например, изменять поведение методов или свойств.
Общий синтаксис наследования классов выглядит следующим образом:
1
2
class SubClass: SuperClass{
}
Для примера наследования рассмотрим простейшую ситуацию. Пусть у нас есть класс человека и класс служащего:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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:
1
2
3
4
var emp: Employee = Employee(name: "Steve", surname: "Jobs", company:"Apple")
var emplInfo = emp.getFullInfo() // Steve Jobs
emp.name = "Tim"
emp.surname = "Cook"
Ключевое слово super
Ключевое слово super позволяет обращаться из подкласса к свойствам и методам базового класса. В выше приведенном примере ключевое слово super используется для обращения к инициализатору базового класса ля передачи ему значений.
Переопределение методов
Подкласс может перенимать полностью функционал базового класса, а может и переопределять его. Для переопределения используется ключевое слово override.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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, мы можем переопределить метод по-другому:
1
2
3
4
override func getFullInfo() -> String{
return "\(super.getFullInfo) - \(self.company)"
}
или
1
2
3
4
override func getFullInfo() -> String{
return super.getFullInfo() + " - \(self.company)"
}
Переопределение свойств
Подобным образом мы можем переопределять свойства:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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.
Переопределение инициализаторов
При переопределении инициализатора необходимо вызвать инициализатор базового класса для инициализации тех свойств, которые определены в базовом классе. Кроме того, если в подклассе есть собственные свойства, их необходимо инициализировать до вызова инициализатора базового класса:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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 мы можем отметить этот инициализатор как обязательный для переопределения в подклассах:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 мы можем запретить переопределение свойств, методов, сабскриптов в производном классе:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:
1
2
3
4
final class User{
//.............
}