Swift中的構(gòu)造函數(shù)及其繼承

??在Swift中,繼承只能發(fā)生在類身上,結(jié)構(gòu)體和枚舉是不能夠被繼承的。一個(gè)類可以繼承另一個(gè)類的方法、屬性和下標(biāo)。當(dāng)一個(gè)類繼承自另一個(gè)類時(shí),這個(gè)類就被稱之為子類,而被繼承的類則被稱之為父類(超類)。子類可以重寫父類的方法、屬性和下標(biāo)。

??在Objective-C中,所有的類都繼承自NSObject,而Swift中的類可以沒(méi)有父類。沒(méi)有父類的類被稱之為基類。NSObject就是Objective-C中所有其它類的基類,而Swift中并沒(méi)有這樣的基類。

??通常情況下,一個(gè)類只能繼承自一個(gè)父類,這被稱之為單繼承。但是,有些時(shí)候一個(gè)類可以繼承自多個(gè)類,這種情況被稱之為多重繼承。Swift中的類只能是單繼承,而多重繼承則是通過(guò)協(xié)議來(lái)實(shí)現(xiàn)的。也就說(shuō),Swift中的類只能繼承自一個(gè)父類,但是它可以遵守多個(gè)協(xié)議,從而達(dá)到類似于多重繼承的效果。

??1、構(gòu)造函數(shù)的繼承

??我們?cè)谏弦黄恼?a href="http://www.itdecent.cn/p/8787e4d11b4c" target="_blank">《Swift中的構(gòu)造方法》里講構(gòu)造函數(shù)代理時(shí),提到過(guò)便利構(gòu)造函數(shù)指定構(gòu)造函數(shù),其實(shí)根據(jù)繼承關(guān)系,類里面的構(gòu)造函數(shù)代理又分為橫向代理向上代理。所謂的橫向代理,就是指發(fā)生在同一個(gè)類中的構(gòu)造函數(shù)代理,也就是我們?cè)谇懊嫠f(shuō)的便利構(gòu)造函數(shù);而向上代理是指子類在構(gòu)造過(guò)程中,要先調(diào)用父類的構(gòu)造函數(shù)來(lái)初始化父類的存儲(chǔ)屬性,這也就是我們所說(shuō)的指定構(gòu)造函數(shù)。

??下面我們來(lái)看一個(gè)具體的示例,在父類Person中,我們通過(guò)調(diào)用它自己的構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)便利構(gòu)造函數(shù),然后在子類Student中調(diào)用指定構(gòu)造函數(shù)來(lái)完成父類存儲(chǔ)屬性的初始化:

// 類的構(gòu)造函數(shù)
class Person {
    var age: Int
    var name: String
    
    // 父類構(gòu)造函數(shù)1:在構(gòu)造函數(shù)中初始化存儲(chǔ)屬性
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    
    // 父類構(gòu)造函數(shù)2:調(diào)用自己的構(gòu)造函數(shù)(便利構(gòu)造函數(shù))
    convenience init(A age: Int, N name: String) {
        self.init(age: age, name: name)
    }
}

class Student: Person {
    var no: Int
    var height: Double
    
    // 子類構(gòu)造函數(shù)1:子類自己的構(gòu)造函數(shù)
    init(age: Int, name: String, no: Int, height: Double) {
        
        // 先初始化自己的存儲(chǔ)屬性
        self.no = no
        self.height = height
        
        // 再向上調(diào)用父類的構(gòu)造函數(shù)1, 對(duì)父類的存儲(chǔ)屬性進(jìn)行初始化(指定構(gòu)造函數(shù))
        super.init(age: age, name: name)
    }
    
    // 子類構(gòu)造函數(shù)2:重寫父類的構(gòu)造函數(shù)
    convenience override init(age: Int, name: String) {
        
        // 調(diào)用自己的構(gòu)造函數(shù)1來(lái)完成初始化工作(便利構(gòu)造函數(shù))
        self.init(age: age, name: name, no: 0, height: 0.0)
    }
}

let p = Person(age: 20, name: "Paul")  // p.age = 20, p.name = Paul
print("p.age = \(p.age), p.name = \(p.name)")

let stu = Student(age: 22, name: "James", no: 1701, height: 1.72)
// stu.age = 22, stu.name = James, stu.no = 1701, stu.height = 1.72
print("stu.age = \(stu.age), stu.name = \(stu.name), stu.no = \(stu.no), stu.height = \(stu.height)")

??構(gòu)造函數(shù)之間的調(diào)用形成了構(gòu)造函數(shù)鏈,而這個(gè)調(diào)用規(guī)則是有一定限制的。具體應(yīng)遵守的規(guī)則如下:

  • 在使用指定構(gòu)造函數(shù)時(shí),必須調(diào)用其直接父類的構(gòu)造函數(shù)。也就是說(shuō),在調(diào)用父類的構(gòu)造函數(shù)初始化父類的存儲(chǔ)屬性時(shí),不能調(diào)用父類的父類的構(gòu)造函數(shù),應(yīng)該調(diào)用直接父類的構(gòu)造函數(shù);
  • 在使用便利構(gòu)造函數(shù)時(shí),必須調(diào)用同一個(gè)類中定義的其它構(gòu)造函數(shù)。這個(gè)應(yīng)該很好理解,不用做過(guò)多的解釋。

??2、構(gòu)造過(guò)程安全檢查

??在Swift中,類的構(gòu)造過(guò)程包含兩個(gè)階段:首先分配內(nèi)存,初始化子類的存儲(chǔ)屬性,然后沿著構(gòu)造函數(shù)鏈往上,初始化父類的存儲(chǔ)屬性,直到最頂端父類的所有存儲(chǔ)屬性都初始化完畢;接著,從構(gòu)造函數(shù)鏈最頂端往下,對(duì)每一個(gè)類的存儲(chǔ)屬性進(jìn)行修改、調(diào)用實(shí)例方法,直到所有的類都處理完畢。Swift的編譯器在構(gòu)造過(guò)程中會(huì)進(jìn)行一些安全檢查,它可以有效的防止存儲(chǔ)屬性在初始化之前被訪問(wèn),還可以防止存儲(chǔ)屬性被另外一個(gè)構(gòu)造函數(shù)賦予不同的值。Swift提供了4中安全檢查機(jī)制,接下來(lái)我將做一一說(shuō)明和演示。

??(1)、指定構(gòu)造函數(shù)必須保證其所在類中所有的存儲(chǔ)屬性都完成初始化之后,才能向上調(diào)用父類的構(gòu)造函數(shù)。也就是說(shuō),如果你先調(diào)用父類的構(gòu)造函數(shù),然后再初始化當(dāng)前類中的存儲(chǔ)屬性,編譯器會(huì)報(bào)錯(cuò):

class Student: Person {
    var no: Int
    var height: Double
    
    init(age: Int, name: String, no: Int, height: Double) {
        
        // 沒(méi)有先初始化自己的存儲(chǔ)屬性,然后再調(diào)用指定構(gòu)造函數(shù),編譯器會(huì)報(bào)錯(cuò)
        super.init(age: age, name: name)  
        
        // 初始化自己的存儲(chǔ)屬性
        self.no = no
        self.height = height
    }

    convenience override init(age: Int, name: String) {

        self.init(age: age, name: name, no: 0, height: 0.0)
    }
}

??(2)、指定構(gòu)造函數(shù)必須先向上調(diào)用父類的構(gòu)造函數(shù),然后再為繼承的屬性設(shè)置新的值,否則指定構(gòu)造函數(shù)賦予的新值將會(huì)被父類構(gòu)造函數(shù)賦予的值給覆蓋。也就是說(shuō),如果你希望給繼承的屬性賦一個(gè)新值,應(yīng)該將賦值語(yǔ)句放在指定構(gòu)造函數(shù)后面,否則賦予的新值將會(huì)被指定構(gòu)造函數(shù)所覆蓋,從而造成賦新值失?。?/p>

class Student: Person {
    var no: Int
    var height: Double

    init(age: Int, name: String, no: Int, height: Double) {
        
        self.no = no
        self.height = height
        
        // 在調(diào)用指定構(gòu)造函數(shù)之前給繼承的屬性age賦新值,這個(gè)會(huì)失敗
        self.age = 35
        
        // 向上調(diào)用父類的構(gòu)造函數(shù), 對(duì)父類的存儲(chǔ)屬性進(jìn)行初始化(指定構(gòu)造函數(shù))
        super.init(age: age, name: name)
        
        // 先調(diào)用指定構(gòu)造函數(shù),然后再為繼承的屬性賦新值,這個(gè)可以成功
        self.name = "Wade"
    }

    convenience override init(age: Int, name: String) {

        self.init(age: age, name: name, no: 0, height: 0.0)
    }
}

??(3)、便利構(gòu)造函數(shù)必須先調(diào)用自己所在類中的其它構(gòu)造函數(shù),然后才能給任意屬性(包括從父類繼承的屬性)賦予新值,否則便利構(gòu)造函數(shù)賦予的新值將會(huì)被同一類中的其它構(gòu)造函數(shù)所覆蓋。也就是說(shuō),如果你想在便利構(gòu)造函數(shù)中給屬性賦新值,則應(yīng)該先調(diào)用其它構(gòu)造函數(shù),然后再為屬性賦新值,否則新值將會(huì)被其它構(gòu)造函數(shù)所覆蓋;

class Student: Person {
    var no: Int
    var height: Double

    init(age: Int, name: String, no: Int, height: Double) {

        self.no = no
        self.height = height

        super.init(age: age, name: name)
    }

    convenience override init(age: Int, name: String) {
        
        // 在便利構(gòu)造函數(shù)中,先給自己的屬性賦新值,然后再調(diào)用其它構(gòu)造函數(shù)
        self.height = 2.999999999  // 這個(gè)會(huì)失敗
        
        // 調(diào)用自己的構(gòu)造函數(shù)完成初始化工作(便利構(gòu)造函數(shù))
        self.init(age: age, name: name, no: 0, height: 0.0)
        
        // 在便利構(gòu)造函數(shù)中,先調(diào)用所在類中的其它構(gòu)造函數(shù),然后再給自己的屬性賦值
        self.no = 188888888  // 這個(gè)可以成功
    }
}

??(4)、構(gòu)造函數(shù)在第一階段完成之前,不能調(diào)用實(shí)例方法,也不能讀取實(shí)例屬性。也就是說(shuō),必須保證子類和父類的所有存儲(chǔ)屬性都被初始化完成之后,才能調(diào)用實(shí)例方法,訪問(wèn)實(shí)例的屬性。

class Student: Person {
    var no: Int
    var height: Double

    init(age: Int, name: String, no: Int, height: Double) {

        self.no = no
        self.height = height
        
        // 指定構(gòu)造方法還沒(méi)有執(zhí)行,父類的屬性還沒(méi)有初始化,這里不能訪問(wèn)父類的屬性
        self.getName(name: name)
        
        // 向上調(diào)用父類的構(gòu)造函數(shù), 對(duì)父類的存儲(chǔ)屬性進(jìn)行初始化(指定構(gòu)造函數(shù))
        super.init(age: age, name: name)
        
        // 指定構(gòu)造方法執(zhí)行完之后,父類的name屬性已經(jīng)被初始化了,這里可以調(diào)用
        self.getName(name: name)
    }

    convenience override init(age: Int, name: String) {

        self.init(age: age, name: name, no: 0, height: 0.0)
    }
    
    // 實(shí)例方法
    func getName(name: String) {
        
        print("name = \(name)")
    }
}

??3、構(gòu)造函數(shù)的繼承

??在Swift中,子類的構(gòu)造函數(shù)有兩種來(lái)源,首先是自己擁有的構(gòu)造函數(shù),其次是從父類中繼承過(guò)來(lái)的構(gòu)造函數(shù)。但是,比不是所有父類構(gòu)造函數(shù)都能夠被子類繼承。子類繼承父類的構(gòu)造函數(shù)是有條件的:

  • 如果子類中沒(méi)有任何構(gòu)造函數(shù),那么它將自動(dòng)繼承父類中所有的指定構(gòu)造函數(shù);
  • 如果子類中實(shí)現(xiàn)了父類所有的指定構(gòu)造函數(shù),那么它都將自動(dòng)繼承父類所有的便利構(gòu)造函數(shù)。

??4、關(guān)鍵字final

??在定義一個(gè)類時(shí),可以使用關(guān)鍵字final聲明類、屬性、方法和下標(biāo)。被final關(guān)鍵字修飾的類不能夠被繼承,被final關(guān)鍵字修飾的屬性、方法和下標(biāo)不能夠被子類重寫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容