Swift 程式語言 — Type Casting
讓我們來看看如何在 Swift 中進行類型轉換吧!
前言:
類型轉換(Type casting)是一種檢查實例類型或將該實例視為其自身類層次結構中其他位置不同的 superclass 或 subclass 的方法。
Swift 中的類型轉換使用 is
和 as
運算符實現。這兩個運算符提供了簡單而富有表現的方式來檢查值的類型或將值轉換為其他類型。
你還可以使用類型轉換來檢查類型是否符合協議,如 Checking for Protocol Conformance 中所述。
|定義類型轉換的 Class 層次結構
你可以使用類型轉換與 class 和 subclass 的層次結構一起使用,以檢查特定 class 實例的類型,並將該實例轉換為相同層次結構的另一個 class。下面三個程式碼片段定義了 class 的層次結構和包含這些 class 實例的陣列,以供類型轉換範例使用。
第一個片段定義了名為 MediaItem
的 base
class
。這個 class
為出現在數位媒體庫中的任何種類的項目提供基本功能。具體來說,它宣告類型為 String
的 name
屬性,以及一個初始名的初始化器。(假定所有媒體項目,包括所有電影跟歌曲,都將具有名稱)
下一個片段定義了 MediaItem
的兩個 subclass。第一個 subclass,Movie
,封裝了電影的額外訊息。它在 MediaItem
的基礎上添加一個 director
的屬性,以及一個相對應的初始化器。第二個 subclass,Song
,在 MediaItem
的基礎上添加了 artist
屬性和對應的初始化器。
最後一個片段創建了一個名為 library
的常數陣列,其中包含兩個 Movie
實例和三個 Song
實例。陣列的類型是在初始化時根據內容推斷出來的。Swift 的類型檢查器可以推斷 Movie
和 Song
具有共同的 superclass,MediaItem
,因此可以推斷 library
為 [MediaItem]
類型。
儲存在 library
中的項目仍然是背後的 Movie
和 Song
實例。但是,如果你遍歷此陣列的內容,則收到項目的類型為 MediaItem
,而不是 Movie
或 Song
。為了將他們作用於本地類型,你需要先檢查他們的類型,或將他們轉換為其他類型,如下所述。
|檢查類型
使用類型檢查運算符(is
)檢查實例是否屬於某個 subclass 類型。如果實例屬於該 subclass 類型,則類型檢查運算符將返回 true
,否則返回 false
。
下面的範例定義了兩個變數 movieCount
和 songCount
,他們計算 library
數組中的 Movie
和 Song
實例的數量:
這個範例遍歷陣列中的所有項目,每次在通過時,for-in
迴圈都會將項目常數設置為陣列中的下一個 MediaItem
。
如果當前的 MediaItem
是 Movie
實例,則 item is Movie
返回 true
,否則返回 false
。同樣的,item is Song
檢查項目是否為 Song
實例。在 for-in
循環的末尾,movieCount
和 songCount
的值包含找到每種類型的 MediaItem
實例的數量。
|向下轉換
某個 class 類型的常數或變數實際上可能是幕後 subclass 的實例。如果你認為是這種情況,可以嘗試使用類型轉換符(as?
或 as!
)將其轉換為 subclass 類型。
由於向下轉換(downcast)可能會失敗,因此類型轉換符採用兩種不同的形式。條件形式 as?
,返回你要向下轉換的類型的 optional 值。強制形式 as!
,嘗試向下轉換和強制將結果作為結合為一個步驟。
當你不確定向下轉換是否成功時,請使用類型轉換運算符(as?
)的條件形式。這種形式的運算符總是返回 optional 值,如果無法向下轉換,則該值將為 nil
。這使你能夠檢查向下轉換是否成功。
僅在確定向下轉換始終成功時,才使用類型轉換運算符(as!
)的強制形式。如果嘗試向下轉換為錯誤的 class 類型,則此形式的運算符將觸發 runtime error。
下面的範例遍歷 library
中的每個 MediaItem
,並為每個項目打印適當的描述。為此,它需要將每個項目作為真實的 Movie
或 Song
,而不是作為 MediaItem
進行訪問。為了使它能夠訪問 Movie
或 Song
的 director
或 artist
屬性以用於描述中,這是必須的。
在此範例中,陣列中的每個項目可能是 Movie
,也可能是 Song
。你事先不知道要為每個項目中使用哪個實際 class
,所以這時使用條件形式的類型轉換符(as?
)來檢查遍歷中每次向下類型轉換:
查看印出的結果:
這個範例首先嘗試將目前項目轉換為 Movie
。由於 item
是 MediaItem
實例,因此可能是 Movie
。同樣的,也可能是 Song
,甚至只是一個基本的 MediaItem
。由於這種不確定性,類型轉換符 as?
嘗試向下轉換為 subclass 類型時返回了一個 optional 值。item as? Movie
的結果為 Movie?
為為 optional Movie
。
當陣列中的 Song
實例使用向下轉換至 Movie
類型時會失敗。為了處理這種情況,上面的例子使用了 Optional binding 來檢查 optional Movie
類型是否包含了一個值(或者說檢查向下類型轉換是否成功)。這個 optional binding 寫作 if let movie = item as ? Movie
,它可以被讀作:「嘗試將 item
作為 Movie
訪問。如果成功,設置一個新的臨時常數 movie
儲存返回的 optional Movie
類型 」。
如果向下轉換成功,則使用 movie
的屬性來印出該 Movie
實例的描述,包括其導演的名稱。每當在 library
中找到 Song
時,將使用同樣的原理來檢查 Song
實例,並印出合適的描述(包括藝術家名稱)。
轉換實際上不會修改實例或更改其值。基礎實例保持不變;簡單的將其視為已強制轉換為其類型的實例並進行訪問。
|類型轉換為 Any & AnyObject
Swift 提供兩種特殊類型來處理非特定類型:
Any
可以表示任何類型的實例,包含函數類型。AnyObject
可以代表任何 class 類型的實例。
僅在明確需要它們的行為和提供功能時,才使用 Any
和 AnyObject
,最好要在程式碼中為使用的類型進行具體說明。
這是一個使用 Any
處理多種不同類型的範例,包括函數類型和 non-class 類型。該範例創建了一個名為 things
的陣列,該陣列可以存儲 Any
類型的值:
things
陣列包含兩個 Int
值,兩個 Double
值,一個 String
值,一個類型為 (Double, Double)
的元組,一個 Movie
實例以及一個接收 String
值並返回 String 值的閉包表達式。
要找出已知為 Any
或 AnyObject
類型的常數或變數的特定類型,可以在 switch
語句的 case
中使用 is
或 as
模式。下面的範例遍歷 things
陣列中的項目,並使用 switch
語句查詢每個項目的類型。switch
語句的幾種情況都將其匹配值綁定到指定類型的常數,使其能夠被印出:
印出結果:
Any 類型代表任何類型的值,包括 optional 類型。如果你使用一個 optional 值期望值是 Any 類型,則 Swift 會出現警告。如果確實要將 optional 值作為 Any 值,則可以使用 as 運算符將 optional 值顯式的轉換為 Any,如下面範例。