默认情况下,swift子类里面的构造器不会继承父类里面的构造器,swift的这种继承机制可以有效的预防一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。
假如你希望自定义的子类中能提供一个或多个跟父类相同的构造器,你可以在子类中提供这些构造器的自定义实现。
当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上 override 修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上override 修饰符.
正如重写属性,方法或者是下标, override修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确
相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器,因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加 override 前缀。
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
下面这个例子就是一个父类和子类的关系 他们所在类中的构造器代理和重写关系
子类Bicycle
继承Vehicle
的属性,其中Init()
是子类的一个指定构造器,子类的指定构造器智能向上调用父类中的指定构造器,所以它会调用super.init()
(父类Vehicle的指定构造器)。在用override
修饰符对子类继承于父类中的属性进行重写,也就是属性numberOfWheels
。 最后在实例化该子类输出numbersOfWheels。
class Bicycle: Vehicle {
// 重写子类的构造器
override init() {
// 重写的子类构造器继承了父类的构造器
super.init()
numberOfWheels = 2
}
}
Let bicycle = Bicycle()
print("Bicycle: \(numberOfWheels.description)")
// 输出:Bicycle: 2 wheel(s)
下面这个例子是另一个子类Hoverboard,该子类本身有一个带有参数的指定构造器。不过该子类要对继承自父类里面的变量属性description
进行重写,并且返回这个属性。用作该子类实例化后的输出,注* 子类可以在构造过程中修改继承自父类的变量属性。
class Hoverboard: Vehicle {
var color: String
// 子类带有参数的的指定构造器
init(color: String) {
self.color = color
// super.init() implicitly called here
}
override var description: String {
return "\(super.description) in a beautiful \(color)"
}
}
let hoverboard = Hoverboard(color: "silver")
print("Hoverboard: \(hoverboard.description)")
就像上面提及的那样,默认情况下,子类是不可能继承父类的构造器的,但是在某种特定条件下,父类的构造器是可以被子类继承的。在实践过程中,也就意味着我们根本不需要再次对父类的构造器进行重写。
假设你为子类中引入的所有新属性都提供了默认值,以下 2 个规则适用:
规则 1
规则 2
即使在子类中添加更多的便利构造器,这两个规则依然适用
下面这个例子展示了在实践中 指定构造器,便利构造器和自动构造器继承,他们三者之间的交互,充分展现了这三个类 Food
,RecipeIngredient
和ShoppingListItem
之间的层级和他们的构造器之间是如何沟通的。
在层级里面的基类(base class)是一个用来封装Foodname的类Food。它有一个单一的String属性。和两个构造器用来提供创建Food实例的。
第一个类
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
let mysteryMeat = Food()
实例化后的类namedMeat
将会引用基类,而该基类有一个指定构造器和一个便利构造器,应为这是一个基类,所以在指定构造器里面不会向上代理父类。还有另一个无参数的便利构造器,此便利构造器横向代理指定构造器,因为这个便利构造器本身会就回构造一个Unnamed
的初始值,而在实例化namedMeat
是首先传值经过便利构造器修改初始值Unnamed
在横向代理给基类的指定构造器,至此整个构造过程已完成了。
实例化mysteryMeat
,但是该实例知识完全引用了 Food类 并没有进行传值代理等过程,所以基类会默认使用便利构造器代理给指定构造器的默认值也就是Unnamed
。实例化后就回得到Unnamed。
下面这个图例就是对第一个类的图例解释,展示了 一个类(基类)里面指定和便利构造器的关系。
第二个类
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
第二个类的实例化和第一个类的实例化比较接近,第二个类RecipeIngredient
它会继承父类Food里面的属性包括构造器,因为在第二个属性的指定构造器里面,根据构造器的代理规则它是向上调用代理类父类Food里面的指定构造器,而第二个便利构造器又进行了重写操作,一个类可以拥有多个便利构造器,便利构造器可以在同一个类里面进行横向代理,而这个类并没有第二个便利构造器,所以不需要便利构造器横向代理。
下面这个图例就是对第二个类的图例解释,展示了 子类和父类里面指定和便利构造器的关系。
第三个类
class ShoppingListItem: RecipeIngredient {
// 布尔值的存储属性
var purchased = false
// 计算属性
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
// 实例化 用数组类型来包含所有的breakfastList里面所有的item
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]
// breakfastList第一个(数组位置第0个)的值是Orange juice
breakfastList[0].name = "Orange juice"
// purchased设为true(默认是false)
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
输出:
1 x Orange juice ✔
1 x Bacon ✘
6 x Eggs ✘