iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 27
0

類型轉換 (Type Casting)

類型轉換是一種檢查實例類型的方法,或者將該實例作為不同的父類或子類從其自己的類層次結構中的其他位置處理。
Swift 中的類型轉換適用 is 和 as 操作符來實現的,這兩個運算符提供一個簡單的而富有表現的方式來檢查一個值的類型或者將一個值賦給另一個類型。

如同協議實現的檢查中描述的那樣,你還可以使用類型轉換來檢查類型是否遵循某個協議。


為類型轉換定義類層次

你可以在類及其子類層次中使用類型轉換來判斷特定類實例的類型並且在同一類層次中將該實例類型轉換為另一個類。

首先我們先定義一個叫做 Student 的 class。這個 class 宣告了一個 String 類型的 name 和一個叫做 init 的 name 初始化器。

// 學校
class School {
    var name:String
    init(name:String) {
        self.name = name
    }
}

之後我們以這個學校的 class 作為下面兩個 class 的父類,分別是 Teacher 和 Student ,他們建立方式相同,都有一個初始化器以及新增一個屬性 (student, teacher):

// 學生
class Students:School {
    var student:String
    init(name:String,student:String) {
        self.student = student
        super.init(name: name)
    }
}

// 老師
class Teachers:School {
    var teacher:String
    init(name:String,teacher:String) {
        self.teacher = teacher
        super.init(name: name)
    }
}

最後我們創建了名為 array 的常數數組,我們新增個兩個 Students 和 Teachers 的實例在其中。 array 數組的類型是在初始化時根據常數字面量推斷出來的。Swift 的類型檢查器能夠推斷 Students 和 Teachers 有一個共同的父類 School ,因此 array 的類型推斷為 [ School ]

let array = [
    Students(name: "XX科技大學", student: "Jeremy"),
    Teachers(name: "OX科技大學", teacher: "Mr.Lee"),
    Students(name: "AC大學", student: "Louis"),
    Teachers(name: "NC大學", teacher: "Mrs. Wu")
]

事實上 array 儲存的項目在後台仍然是 Students 和 Teachers 實例。如果你遍歷這個數組的內容,你取出的項目將會是 School 類型而非上面兩者類型。為了使用他們原生的類型,你需要檢查他們的類型或將他們向下轉換為不同的類型。


類型檢查

使用類型檢查操作符 is 來檢查一個實例是否屬於一個特定的子類。如果實例是該子類類型,類型檢查操作符返回 true ,否則返回 false 。下面我們定義了兩個變數, teachersCount 和 studentsCount ,用來計算數組 array 中 Teachers 和 Students 實例的數量:

var teachersCount = 0
var studentsCount = 0

for person in array {
    if person is Teachers{
        teachersCount += 1
    }else if person is Students{
        studentsCount += 1
    }
}

print("教師人數共有\(teachersCount)人,學生人數共有\(studentsCount)人。")

如果當前 person 是 Teachers 類型的實例, item is Teachers 返回 true , teachersCount 加上 1 ,反之返回 false 。 Students 也是同樣的。在 for - in 循環的最後,變數 teachersCount 和 studentsCount 的值就是數組中對應類型實例的數量。


向下類型轉換

某個類類型的常數或變數可能實際上指的是後台的子類實例,當你遇到這種情況時,你可以嘗試使用類型別換運算符( as? 或 as! ) 將他向下轉換為子類型。

因為向下轉換可能會失敗,所以類型轉換運算符有兩種不同的形式。條件形式, as? ,返回了一個你將要向下類型轉換的值的可選項。強制形式, as! ,則將向下類型轉換和強制展開結合為一個步驟。

如果不確定向下轉換是否能成功,那麼就使用類型轉換運算符的條件形式 as? ,這種形式的操作符將始終返回一個可選項,如果向下轉換為失敗的話,值將會為 nil ,這使你能夠檢查一個向下轉換是否成功。

當你確定向下轉換類型會成功時,使用強制形式的類型轉換操作符 as!。當你向下轉換至一個錯誤的類型時,強制形式的類型轉換操作符會觸發一個運行錯誤。

我們使用上面的範例加上類型轉換符來檢查遍歷中每次向下類型轉換,因為你不知道遍歷時項目的確切類型是什麼所以我們使用條件形式的 as? ,我們希望能輸出該實例的內容。因此每個項目均需要被當做 Teachers 或 Students 來訪問,而不僅僅是 School 。為了在描述信息中訪問 Teachers 或 Students 的 teacher 和 student 屬性,這樣做是必要的。

array 中每一個項目的類型可能是 Students 也可能是 Teachers 。你不知道遍歷時項目的確切類型是什麼,所以這時使用條件形式的類型轉換符 as? 來檢查遍歷中每次向下類型轉換:

for person in array {
    if let teacher = person as? Teachers{
        print("學校為:\(teacher.name),教師名稱為:\(teacher.name)")
    } else if let student = person as? Students {
        print("學校為:\(student.name),學生名稱為:\(student.name)")
    }
}

我們使用 person 當做 School 向下類型轉換。由於 person 是一個 School 的實例,它有可能是 Teachers ,也可能是 Students 或者僅僅是 School 。介於這種不確定的性質,類型轉換符 as? 在向下類型轉換到子類時返回了一個可選項。 as? Teachers 的結果是 Teachers? 類型,也就是可選的 Teachers 類型。

數組中的 Students 實例使用向下轉換至 Teachers 類型時會失敗。為了處理這種情況,上面的例子使用了可選綁定來檢查可選 Teachers 類型是否包含了一個值。這個可選綁定寫作“ if let teacher = person as? Teachers”,它可以被讀作:“ 嘗試以 Teachers 類型訪問 person。如果成功,設置一個新的臨時常量 teacher 儲存返回的可選 Teachers 類型。 ”

如果向下類型轉換成功, teacher 的屬性將輸出 Teachers 實例的訊息(name 和 teacher),當然 student 也如同上面一樣,輸出其實例訊息(name 和 student),結果如下:

https://ithelp.ithome.com.tw/upload/images/20180115/20107701LqAeZxA251.png

類型轉換實際上不會改變實例及修改其值。實例不會改變;它只是將它當做要轉換的類型來訪問。


Any 和 AnyObject 的類型轉換

Swift 提供了兩種特殊類型來處理不確定的類型:

  • Any 可以表示任何類型的實例,包括函數類型。
  • AnyObject 可以表示任何類類型的實例。

只有當你明確需要他們提供的行為和功能時才使用Any和AnyObject。在寫代碼時使用更加明確的類型表達總要好一些。

我們定義了一個名為 things 的數組,它用於儲存 Any 類型的值:

var items = [Any]()

items.append(0)
items.append(0.0)
items.append("Jeremy")
items.append(Teachers(name: "OP大學", teacher: "Mr. Xue"))
items.append({ (name: String) -> String in "閉包測試: \(name)" })

之後我們使用 switch 的case中使用 is 和 as 操作符找出已知 Any 類型的常數或變數的具體類型。下面的例子使用 switch 語句遍歷了 items 數組並查詢每一項的類型。其中部分的 case 將確定的值和確定類型的常數綁定在一起,讓他的值可以被輸出:

for item in items{
    switch  item {
    case is Int:
        print("0 是整數")
    case 0 as Int:
        print("0 是 Int ")
    case 0 as Double:
        print("0.0 是 Double")
    case let name as String:
        print("\(name),你好")
    case let teacher as Teachers:
        print("大學名稱為:\(teacher.name),教師名稱為:\(teacher.teacher)")
    case let closureString as (String) -> String:
        print(closureString("我是閉包"))
    default:
        print("我沒有設定這種 case ")
    }
}

結果如下:
https://ithelp.ithome.com.tw/upload/images/20180115/20107701QqkanZwTzk.png

Any 類型表示了任意類型的值,包括可選類型。如果使用顯式的宣告 Any 類型使用可選項,Swift 就會發出警告。如果你需要在 Any 值中使用可選項,你可以使用 as 運算符來顯式地轉換可選項為Any。

let optionalNum:Int? = 3
items.append(optionalNum) // 錯誤
items.append(optionalNum as Any) // 正確

上一篇
Day-26 Swift 語法(22) - 錯誤處理 Error Handling
下一篇
Day-28 Swift 語法(24) - 擴展 Extensions
系列文
Swift 菜鳥的30天30

尚未有邦友留言

立即登入留言