1. 程式人生 > >Swift:繼承專題

Swift:繼承專題

1. 多型性

import UIKit

class Avatar { // 角色
    var name: String
    var life = 100 {
        didSet { //  這裡一定只能用didSet,在改變之後再次改變life的值,如果用willSet只能在內部改變,出去之後還是原樣
            if life > 100 {
                life = 100
            }
        }
    }
    var isAlive: Bool {
        return life <= 0 ? false : true
    }
    
    init(name: String) {
        self.name = name
    }
    
    func beAttacked(attack: Int) {
        life -= attack
        if life < 0 {
            life = 0
        }
    }
}

class User: Avatar {
    var score = 0
    var level = 0
    
    func getScore(score: Int) {
        self.score += score
        if score > level * 100 { // 升級
            level += 1
        }
    }
}

final class Magician: User {
    var magic = 100
    func heal(user: User) { // 魔法師可以給User下面的所有子類治療
        user.life += 200
    }
}

class Warrior: User {
    var weapon: String? // 可以不拿武器
}

class Monster: Avatar {
    func attack(user: User, amount: Int) {
        user.beAttacked(attack: amount)
    }
}

final class Zombie: Monster { // 殭屍
    var type: String = "Default"
}

let player1 = Magician(name: "aaa") // 也是User的物件,也是Avatar的物件
let player2 = Warrior(name: "bbb")
let zombie = Zombie(name: "ccc")
let monster = Monster(name: "ddd")

func printBasicInfo(avatar: Avatar) {
    print("名字是\(avatar.name),生命值是\(avatar.life),\(avatar.isAlive == true ? "活著" : "死了")")
}

printBasicInfo(avatar: player1)
printBasicInfo(avatar: player2)
printBasicInfo(avatar: zombie)
printBasicInfo(avatar: monster)

let avatar: [Avatar] = [player1, player2, zombie, monster] // 陣列元素的型別是Avatar,可是存放的是Avatar的子類
for avatar in avatar {
    avatar.beAttacked(attack: 110)
}

printBasicInfo(avatar: player1)
printBasicInfo(avatar: player2)
printBasicInfo(avatar: zombie)
printBasicInfo(avatar: monster)

player1.heal(user: player1)
player1.heal(user: player2)
print(player1.life)
printBasicInfo(avatar: player1)
printBasicInfo(avatar: player2)


2. 兩段式構造

在把所有屬性全部初始化之前,不允許有邏輯涉及self自身

Ps,必須先初始化子類自己的屬性,再呼叫父類的建構函式

import UIKit

class A {
    var x: Int
    var y: Int
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

class B: A {
    var z: Int
    init(x: Int, y: Int, z: Int) {
        // 構造初值
        self.z = z
        super.init(x: x, y: y)
        
        // 構造初值完畢,進行後續邏輯
        decX()
    }
    
    func decX()
    {
        x -= 1
    }
}

3. 建構函式和普通函式一樣是可以有預設引數的,可以在括號裡面就給引數賦初值

4. convenience關鍵字,用於修飾init建構函式,表示該建構函式裡面還可以呼叫該類中其他的建構函式

5. convenience關鍵字修飾的函式中不允許出現super.init,必須要依靠一個指定的建構函式來實現

6.  建構函式的繼承有兩個規則

規則一:如果子類實現了父類所有指定建構函式,則自動繼承父類的所有便利建構函式

程式碼如下

import UIKit

class A {
    var x: Int
    var y: Int
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
    convenience init(x: Int) {
        let y = 6
        self.init(x: x, y: y)
    }
    
    func printHello() {
        print("Hello!")
    }
}

class B: A { // B實現了父類的所有指定建構函式,因此B的例項可以呼叫A的所有指定建構函式和便利建構函式
    override init(x: Int, y: Int) {
        let xx = 2 * x
        let yy = 2 * y
        super.init(x: xx, y: yy)
    }
}

let b = B(x: 10)

解釋,這裡傳入x為10,用的是父類的便利建構函式,因此先呼叫父類的便利建構函式,則此時x為10,y為6,由於在函式的最後還寫了self.init(x: x, y: y),說明還要調本類中的建構函式,這裡的類指的是父類,即A,但是在B中原有的建構函式被過載了,因此呼叫的其實是過載之後的建構函式,因此全部double啦,變成20和12!

下面來看一段程式碼

import UIKit

class A {
    var x: Int
    var y: Int
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
    convenience init(x: Int) {
        let y = 6
        self.init(x: x, y: y)
    }
    
    init() {
        self.x = 100
        self.y = 200
    }
    
    func printHello() {
        print("Hello!")
    }
}

class B: A { // B實現了父類的所有指定建構函式,因此B的例項可以呼叫A的所有指定建構函式和便利建構函式
    override init(x: Int, y: Int) {
        let xx = 2 * x
        let yy = 2 * y
        super.init(x: xx, y: yy)
    }

}

let b = B(x: 6)
b.x
b.y

我僅僅是在A中又加了一個指定的建構函式init(),傳入為空,編譯器就開始報錯了。

報錯的原因在於不滿足規則一,B並沒有過載A類所有的指定建構函式,A中指定的建構函式有兩個,B卻只實現了一個,因此,B也不會繼承A中其他的便利函式。

如果在B中加入這樣幾行程式碼

override init() {
    print("Hello!")
    super.init()
}

就不報錯了,因為這個時候B重寫了A所有的指定建構函式,所以也自動繼承了A的所有便利建構函式。

Ps:子類指定的建構函式一定要用super.init,不然肯定報錯!

規則二:如果子類沒有實現(這裡的實現我認為是以過載方式去實現)父類的任何指定建構函式,則自動繼承父類所有的指定建構函式

程式碼如下

import UIKit

class A {
    var x: Int
    var y: Int
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
    convenience init(x: Int) {
        let y = 6
        self.init(x: x, y: y)
    }
    
    init() {
        self.x = 100
        self.y = 200
    }
    
    func printHello() {
        print("Hello!")
    }
}

class B: A {
}

let b1 = B()
let b2 = B(x: 1)
let b3 = B(x: 1, y: 2)

這裡B沒有過載A的任何指定的建構函式,因此自動繼承A的所有指定的建構函式,同時,根據規則一,如果子類實現了父類所有的指定建構函式,則自動繼承父類的所有便利建構函式,因此你看b2那一行程式碼也是不報錯的

7. 子類制定的建構函式一定要呼叫super.init,不然一定會報錯

8. required 關鍵字,如果修飾父類的建構函式,表示該類的子類一定要實現(我理解為過載)該建構函式,並且父類和子類的都要用required關鍵字,子類不能寫override。

9. 結構體因為沒有繼承,所以沒有指定的建構函式和便利的建構函式一說。