Swift:繼承專題
阿新 • • 發佈:2018-12-12
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)