与其他面向对象语言一样,Swift 也支持类继承的概念。
继承允许我们从现有类创建新类。
创建的新类称为子类(或派生类),而从中派生子类的现有类称为超类(或基类)。
Swift 继承语法
在 Swift 中,我们使用冒号:
来从另一个类继承一个类。例如,
// define a superclass
class Animal {
// properties and methods definition
}
// inheritance
class Dog: Animal {
// properties and methods of Animal
// properties and methods of Dog
}
在这里,我们从Animal
超类继承了Dog
子类。
示例:Swift 继承
class Animal {
// properties and method of the parent class
var name: String = ""
func eat() {
print("I can eat")
}
}
// inherit from Animal
class Dog: Animal {
// new method in subclass
func display() {
// access name property of superclass
print("My name is ", name);
}
}
// create an object of the subclass
var labrador = Dog()
// access superclass property and method
labrador.name = "Rohu"
labrador.eat()
// call subclass method
labrador.display()
输出
I can eat My name is Rohu
在上面的示例中,我们从Animal
超类派生了Dog
子类。请注意以下语句:
labrador.name = "Rohu"
labrador.eat()
在这里,我们使用labrador
(Dog
类的对象)来访问Animal
类中的name
和eat()
。这是可能的,因为子类继承了超类的所有属性和方法。
此外,我们在Dog
类的`eat()`方法内访问了`name`属性。
is-a 关系
在 Swift 中,继承是一种“is-a”关系。也就是说,只有当两个类之间存在“is-a”关系时,我们才使用继承。例如,
- **汽车**是**交通工具**
- **苹果**是**水果**
- 猫是动物
在这里,汽车可以从交通工具继承,苹果可以从水果继承,等等。
Swift 继承中的方法重写
在之前的示例中,我们看到子类的对象可以访问超类的方法。
但是,如果超类和子类中都存在相同的方法该怎么办?
在这种情况下,子类中的方法会重写超类中的方法。这个概念在 Swift 中称为方法重写。
我们使用override
关键字来告知编译器我们正在重写一个方法。
示例:方法重写
class Animal {
// method in the superclass
func eat() {
print("I can eat")
}
}
// Dog inherits Animal
class Dog: Animal {
// overriding the eat() method
override func eat() {
print("I eat dog food")
}
}
// create an object of the subclass
var labrador = Dog()
// call the eat() method
labrador.eat()
输出
I eat dog food
在上面的示例中,Dog
类和Animal
类中都存在相同的eat()
方法。
现在,当我们使用Dog
子类的对象调用eat()
方法时,会调用Dog
类中的方法。
这是因为Dog
子类的eat()
方法重写了Animal
超类中的相同方法。我们使用了override
关键字来指定该方法已被重写。
override func eat() {
print("I eat dog food")
}
Swift 继承中的 `super` 关键字
之前我们看到,子类中的相同方法会重写超类中的方法。
但是,如果我们想从子类访问超类的方法,我们使用super
关键字。例如,
class Animal {
// create method in superclass
func eat() {
print("I can eat")
}
}
// Dog inherits Animal
class Dog: Animal {
// overriding the eat() method
override func eat() {
// call method of superclass
super.eat()
print("I eat dog food")
}
}
// create an object of the subclass
var labrador = Dog()
// call the eat() method
labrador.eat()
输出
I can eat I eat dog food
在上面的示例中,Dog
子类的eat()
方法重写了Animal
超类中的相同方法。
在Dog
类中,我们使用了
// call method of superclass
super.eat()
从Dog
子类调用Animal
超类中的eat()
方法。
因此,当我们使用labrador
对象调用eat()
方法时,
// call the eat() method
labrador.eat()
eat()
方法的重写版本和超类版本都会被执行。
为什么要继承?
为了理解继承的好处,让我们考虑一个场景。
假设我们正在处理正多边形,如正方形、五边形等。并且,我们需要根据输入来计算这些多边形的周长。
1. 由于计算周长的公式对于所有正多边形都是通用的,我们可以创建一个Polygon
类和一个calculatePerimeter()
方法来计算周长。
class RegularPolygon {
calculatePerimeter() {
// code to compute perimeter
}
}
2. 并从RegularPolygon
类继承Square
和Pentagon
类。这些类中的每一个都将具有存储边长和边数的属性,因为它们对于所有多边形都不同。
class Square: RegularPolygon {
var length = 0
var sides = 0
}
我们将length
和sides
的值传递给calculatePerimeter()
来计算周长。
这就是继承如何使我们的代码可重用且更直观。
示例:继承的好处
import Foundation
class RegularPolygon {
func calculatePerimeter(length: Int, sides: Int) {
var result = length * sides
print("Perimeter:", result )
}
}
// inherit Square from Polygon
class RegularSquare: RegularPolygon {
var length = 0
var sides = 0
func calculateArea() {
var area = length * length
print("Regular Square Area:", area)
}
}
// inherit Pentagon from Polygon
class RegularTriangle: RegularPolygon {
var length = 0.0
var sides = 0.0
func calculateArea() {
var area = (sqrt(3)/4) * (length * length)
print("Regular Triangle Area:", area)
}
}
var shape = RegularSquare()
shape.length = 4
shape.calculateArea()
shape.calculatePerimeter(length: 3,sides:4)
var shape2 = RegularTriangle()
shape2.length = 2
shape2.calculateArea()
shape2.calculatePerimeter(length: 2,sides:3)
输出
Regular Square Area: 16 Perimeter: 12 Regular Triangle Area: 1.7320508075688772 Perimeter: 6
在上面的示例中,我们创建了一个RegularPolygon
类,用于计算正多边形的周长。
在这里,RegularSquare
和RegularTriangle
继承自RegularPolygon
。
正多边形周长的计算公式对所有多边形都通用,因此我们重用了超类的calculatePerimeter()
方法。
由于面积的计算公式因形状而异,因此我们在子类中创建了一个单独的方法来计算面积。