#27 init的那些小事 part2

Jeff Lin
6 min readSep 6, 2023

--

前言:這篇是init西方取經時瞭解到的,因為跟上一篇狗狗事件沒關係,所以挪出來額外紀錄。

initializer的自動繼承

在物件繼承時,會獲得父類別的屬性以及方法,然而init卻不見得會繼承到子類別,要讓父類別的init自動被繼承需要滿足幾個條件,官方文件的說明如下:

1. If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

如果子類別沒有定義任何designated initializers,他會自動繼承父類別的designed initializer。

表示當今天子類別沒有再新增任何不含值的屬性時,就不需要再自定義designated initializers去賦值給新的屬性,因此就可以繼承父類別的designed initializer。

以剛剛的例子示範:

class File {
var filename: String
var data: String

init(filename: String, data: Any) {
self.filename = filename
self.data = String(describing: data)
}
}

class VersionedFile:File {
var version:Double = 1
convenience init(filename: String, data: Any, version:Double){
self.init(filename: filename, data: data)
self.version = version
}
}

子類別VersionedFile新增的屬性version已經有預設值1,因此不需要再自定義init,此時便可繼承父類別的所有designated initializers。由下圖可知,init(filename:data:)是繼承自父類別的建構子。另外也可以觀察到,就算有定義convenience initializers,也不會影響繼承。

我如果在子類別自定義designated initializer,則繼承的init就會消失

class VersionedFile:File {
var version:Double = 1
//重新自定義designated initializer(新增createdDate參數)
init(filename: String, data: Any, createdDate:Double) {
super.init(filename: filename, data: data)
}

convenience init(filename: String, data: Any, version:Double){
self.init(filename: filename, data: data, createdDate: 0)
self.version = version
}
}

由圖可知,繼承的init(filename:data:) 已經被自定義的init給覆蓋掉,變成init(filename:data:version:)

2. If your subclass provides an implementation of all of its superclass designated initializers — either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition — then it automatically inherits all of the superclass convenience initializers.

如果子類有繼承或客製化父類”所有的”designated initializers,則子類就可獲得父類的convenience initializers。

以實際例子示範,首先,先定義一個Food類別物件,並自定義init初始化屬性。

class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}

定義RecipeIngredient的類別,並繼承Food。接著自定義init來將自己的屬性quantity賦值,最後呼叫super.init將繼承來的屬性賦值。

class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
//將自己的屬性quantity賦值
self.quantity = quantity
//將將繼承來的屬性賦值
super.init(name: name)
}

接著初始化物件,看看建構子如何:

可以發現自定義init後,根據自動繼承的規則(rule 1),將無法繼承父類別的所有的initializers(designated initializers與convenience initializers)。

但如果將父類別的所有designated initializers 客製(custom implementation),便可以符合自動繼承的規則(rule 2),將可以繼承父類別的convenience initializers,如下面所示:

class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
//將父類別的designated initializer進行custom implementation
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}

--

--

Jeff Lin
Jeff Lin

Written by Jeff Lin

一個喜歡彈吉他、寫 Swift 的人,所以手指沒有閒下來的一天

Responses (1)