Swift 程式語言 — Type Casting

讓我們來看看如何在 Swift 中進行類型轉換吧!

Jeremy Xue
Jeremy Xue ‘s Blog
8 min readOct 25, 2019

--

Photo by Diomari Madulara on Unsplash

前言:

類型轉換(Type casting)是一種檢查實例類型或將該實例視為其自身類層次結構中其他位置不同的 superclass 或 subclass 的方法。

Swift 中的類型轉換使用 isas 運算符實現。這兩個運算符提供了簡單而富有表現的方式來檢查值的類型或將值轉換為其他類型。

你還可以使用類型轉換來檢查類型是否符合協議,如 Checking for Protocol Conformance 中所述。

|定義類型轉換的 Class 層次結構

你可以使用類型轉換與 class 和 subclass 的層次結構一起使用,以檢查特定 class 實例的類型,並將該實例轉換為相同層次結構的另一個 class。下面三個程式碼片段定義了 class 的層次結構和包含這些 class 實例的陣列,以供類型轉換範例使用。

第一個片段定義了名為 MediaItembase class。這個 class 為出現在數位媒體庫中的任何種類的項目提供基本功能。具體來說,它宣告類型為 Stringname 屬性,以及一個初始名的初始化器。(假定所有媒體項目,包括所有電影跟歌曲,都將具有名稱)

下一個片段定義了 MediaItem 的兩個 subclass。第一個 subclass,Movie,封裝了電影的額外訊息。它在 MediaItem 的基礎上添加一個 director 的屬性,以及一個相對應的初始化器。第二個 subclass,Song,在 MediaItem 的基礎上添加了 artist 屬性和對應的初始化器。

最後一個片段創建了一個名為 library 的常數陣列,其中包含兩個 Movie 實例和三個 Song 實例。陣列的類型是在初始化時根據內容推斷出來的。Swift 的類型檢查器可以推斷 MovieSong 具有共同的 superclass,MediaItem,因此可以推斷 library[MediaItem] 類型。

儲存在 library 中的項目仍然是背後的 MovieSong 實例。但是,如果你遍歷此陣列的內容,則收到項目的類型為 MediaItem,而不是 MovieSong。為了將他們作用於本地類型,你需要先檢查他們的類型,或將他們轉換為其他類型,如下所述。

|檢查類型

使用類型檢查運算符(is)檢查實例是否屬於某個 subclass 類型。如果實例屬於該 subclass 類型,則類型檢查運算符將返回 true,否則返回 false

下面的範例定義了兩個變數 movieCountsongCount,他們計算 library 數組中的 MovieSong 實例的數量:

這個範例遍歷陣列中的所有項目,每次在通過時,for-in 迴圈都會將項目常數設置為陣列中的下一個 MediaItem

如果當前的 MediaItemMovie 實例,則 item is Movie 返回 true,否則返回 false。同樣的,item is Song 檢查項目是否為 Song 實例。在 for-in 循環的末尾,movieCountsongCount 的值包含找到每種類型的 MediaItem 實例的數量。

|向下轉換

某個 class 類型的常數或變數實際上可能是幕後 subclass 的實例。如果你認為是這種情況,可以嘗試使用類型轉換符(as?as!)將其轉換為 subclass 類型。

由於向下轉換(downcast)可能會失敗,因此類型轉換符採用兩種不同的形式。條件形式 as?,返回你要向下轉換的類型的 optional 值。強制形式 as!,嘗試向下轉換和強制將結果作為結合為一個步驟。

當你不確定向下轉換是否成功時,請使用類型轉換運算符(as?)的條件形式。這種形式的運算符總是返回 optional 值,如果無法向下轉換,則該值將為 nil。這使你能夠檢查向下轉換是否成功。

僅在確定向下轉換始終成功時,才使用類型轉換運算符(as!)的強制形式。如果嘗試向下轉換為錯誤的 class 類型,則此形式的運算符將觸發 runtime error。

下面的範例遍歷 library 中的每個 MediaItem,並為每個項目打印適當的描述。為此,它需要將每個項目作為真實的 MovieSong,而不是作為 MediaItem 進行訪問。為了使它能夠訪問 MovieSongdirectorartist 屬性以用於描述中,這是必須的。

在此範例中,陣列中的每個項目可能是 Movie,也可能是 Song。你事先不知道要為每個項目中使用哪個實際 class,所以這時使用條件形式的類型轉換符(as?)來檢查遍歷中每次向下類型轉換:

查看印出的結果:

這個範例首先嘗試將目前項目轉換為 Movie。由於 itemMediaItem 實例,因此可能是 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 類型的實例。

僅在明確需要它們的行為和提供功能時,才使用 AnyAnyObject,最好要在程式碼中為使用的類型進行具體說明。

這是一個使用 Any 處理多種不同類型的範例,包括函數類型和 non-class 類型。該範例創建了一個名為 things 的陣列,該陣列可以存儲 Any 類型的值:

things 陣列包含兩個 Int 值,兩個 Double 值,一個 String 值,一個類型為 (Double, Double) 的元組,一個 Movie 實例以及一個接收 String 值並返回 String 值的閉包表達式。

要找出已知為 AnyAnyObject 類型的常數或變數的特定類型,可以在 switch 語句的 case 中使用 isas 模式。下面的範例遍歷 things 陣列中的項目,並使用 switch 語句查詢每個項目的類型。switch 語句的幾種情況都將其匹配值綁定到指定類型的常數,使其能夠被印出:

印出結果:

Any 類型代表任何類型的值,包括 optional 類型。如果你使用一個 optional 值期望值是 Any 類型,則 Swift 會出現警告。如果確實要將 optional 值作為 Any 值,則可以使用 as 運算符將 optional 值顯式的轉換為 Any,如下面範例。

--

--

Jeremy Xue
Jeremy Xue ‘s Blog

Hi, I’m Jeremy. [好想工作室 — iOS Developer]