前面第3課提過,「函式」的作用是當作一般化的工具,就像自動販賣機,不需瞭解內部怎麼運作,只要知道怎麼用就行。「物件類型」也有類似的作用,而且物件類型的作用更多樣,更豐富。

例如,我們可以將1-8a「商王年表」物件類型加以一般化,成為「歷代帝王年表」,讓App內容不拘限於商朝,能學習更多歷史。我們也利用這個機會,學習物件另一個重要觀念:物件初始化。

// 1-8c struct: 歷代帝王年表
// Revised by Heman, 2021/07/28

struct 帝王 {
    var 朝代: String
    var 名號: String
    var 即位: Int        //西元年
    var 在位: Int        //年

    init(_ 朝: String = "", 名: String = "", 即: Int = 0, 在: Int = 0) {
        朝代 = 朝
        名號 = 名
        即位 = 即
        在位 = 在
    }

    func 自我介紹() -> String {
        let 西元年 = 即位 < 0 ? "西元前\\(abs(即位))年" : "西元\\(即位)年"
        let 介紹詞 = "我是\\(朝代)代帝王「\\(名號)」,\\(西元年)即位,在位\\(在位)年。"
        return 介紹詞
    }
}

let 乞丐 = 帝王()
let 商鞅 = 帝王("商")
let 商湯 = 帝王("商", 名: "大乙湯", 即: -1558, 在: 12)

for 角色 in [乞丐, 商鞅, 商湯] {
    print(角色.自我介紹())
}

在資料的結構部分,我們暫只增加一個屬性:朝代,將商王年表推廣為歷代帝王年表。

另外增加了一個非常特別的「函式」稱為 init(),這是 "initializer" (初始化函式)的簡寫。這個函式特別的地方,在於「不用 func 宣告,而且不要任何回傳值」。

初始化函式 init() 最重要的功能,就是必須把所有的物件屬性「初始化」,也就是指定初始值。

所以這裡的 init() 需要4個參數:朝、名、即、在,分別對應4個屬性:朝代、名號、即位、在位。而這4個參數我們都給了「預設值」,前面提過,如果函式參數有預設值,那麼在呼叫時,這個參數是可以省略的。

所以我們可以這樣來使用物件類型:

let 乞丐 = 帝王()

不需輸入任何參數,全部用預設值來初始化物件的屬性。也不用特別寫出 init(),每次使用物件類型就會自動呼叫初始化函式。

而且再注意到 init() 第一個參數名稱前面有加底線,這代表呼叫時,這個參數名稱可省略,所以只要給參數值就可以:

let 商鞅 = 帝王("商")

如果要帶入所有的參數值,最完整的呼叫方式就類似之前的做法,但是改用 init() 的參數名稱,而不是物件的屬性名稱,同時第一個參數名稱可省略:

let 商湯 = 帝王("商", 名: "大乙湯", 即: -1558, 在: 12)