天天看點

Swift程式設計十九(類型轉換)類型轉換

案例代碼下載下傳

類型轉換

類型轉換是一種檢查執行個體類型的方法,或者将該執行個體視為與其自己的類層次結構中的其他位置不同的超類或子類。

Swift中的類型轉換是使用is和as運算符實作的。這兩個運算符提供了一種簡單而富有表現力的方法來檢查值的類型或将值轉換為其他類型。

還可以使用類型轉換來檢查類型是否符合協定,如檢查協定一緻性中所述。

為類型轉換定義類層次結構

可以使用類型轉換來檢查特定類執行個體在類和子類的層次結構中的類型,并将該執行個體轉換為同一層次結構中的另一個類。下面的三個代碼片段定義了類的層次結構和包含這些類的執行個體的數組,用于類型轉換的示例。

第一個片段定義了一個名為MediaItem的新基類。此類為數字媒體庫中顯示的任何類型的項目提供基本功能。具體來說,它聲明了一個String類型的屬性name和一個初始化器。(假設所有媒體項目,包括所有電影和歌曲,都會有一個名字。)

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
           

下一個片段定義了兩個MediaItem子類。第一個子類Movie包含有關電影或電影的其他資訊。它在MediaItem基類的基礎上添加了一個director屬性,并帶有相應的初始化程式。第二個子類Song,在基類之上添加一個artist屬性和初始值設定項:

class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

class Song: MediaItem {
    var artist: String
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}
           

最後一個片段建立一個名為library的常量數組,其中包含兩個Movie執行個體和三個Song執行個體。library通過使用數組文字的内容初始化數組來推斷數組的類型。Swift的類型檢查器能夠推導出Movie和Song具有一個共同的超類MediaItem,是以它推斷出library是[MediaItem]類型的數組:

let library = [
    Movie(name: "Casablanca", director: "Michael Curtiz"),
    Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
    Movie(name: "Citizen Kane", director: "Orson Welles"),
    Song(name: "The One And Only", artist: "Chesney Hawkes"),
    Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
           

用兩個Movie執行個體和三個Song執行個體建立library常量數組。但是,如果疊代此數組的内容,則收到的項目将被鍵入MediaItem,而不是Movie或Song。要将它們作為本類型使用,需要檢查其類型,或将它們向下轉換為其他類型,如下所述。

檢查類型

使用類型檢查運算符(is)來檢查執行個體是否屬于某個子類類型。如果執行個體屬于該子類類型,則類型檢查操作符傳回true,如果不是,則傳回false。

下面的例子定義兩個變量,movieCount和songCount,其計數library數組中Movie和Song執行個體的數量:

var movieCount = 0
var songCount = 0
for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
           

此示例周遊library數組中的所有項。每次通過for-in循環将item常量設定為MediaItem數組中的下一個。

如果目前MediaItem是Movie執行個體傳回傳回ture,如果不是,則傳回false。同樣,檢查item是否為Song執行個體。在for-in循環結束時,movieCount和songCount值表示含有各自類型計數的多少。

類型轉換

某個類類型的常量或變量實際上可能是子類的執行個體。在這種情況下,可以嘗試使用類型轉換運算符(as?或as!)向下轉換為子類類型。

由于向下轉換可能會失敗,是以類型轉換運算符有兩種不同的形式。條件形式as?,傳回您嘗試向下轉換的類型的可選值。強制形式as!嘗試向下轉換并強制轉換結果。

當不确定向下轉換是否成功時,請使用類型轉換運算符(as?)的條件形式。這種形式的運算符将始終傳回一個可選值,如果無法進行向下轉換,則該值将為nil。這需要堅持轉換是否成功。

僅當确定向下轉換将始終成功時,才使用類型轉換運算符(as!)的強制形式。如果嘗試向下轉換為不正确的類類型,則此形式的運算符将觸發運作時錯誤。

下面的例子在library中疊代每個MediaItem,并列印每個項目的相應說明。要做到這一點,它需要将每個項目作為一個真實Movie或Song,而不僅僅作為一個MediaItem。這是為了能夠通路Movie的director屬性或Song的artist屬性在說明中使用。

在此示例中,數組中的每個項可能是Movie,或者可能是Song。事先并不知道每個項目使用哪個實際類,是以使用類型轉換(as?)的條件形式來檢查每次循環時的向下轉換為合适的operator:

for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: \(song.name), by \(song.artist)")
    }
}
           

該示例首先嘗試将目前item向下轉換為Movie。因為item是一個MediaItem例子,這是可能的,它可能是一個Movie; 同樣,它也可能是一個Song,甚至隻是一個MediaItem。由于這種不确定性,類型轉換運算符的形式as?在嘗試向下轉換為子類類型時傳回一個可選值。item as? Movie結果是Movie?類型或“optional Movie”。

數組中的Movie執行個體轉換為Song執行個體時,向下轉換失敗。為了解決這個問題,上面的示例使用可選綁定來檢查可選Movie實際上是否包含一個值(即,類型轉換是否成功。)此可選綁定寫為“if let movie = item as? Movie”,可以讀作:

“嘗試轉換item為Movie。如果成功,則設定一個新的臨時常量存儲在傳回的可選Movie中。”

如果向下轉換成功,movie則使用屬性來列印該Movie執行個體的描述,包括其名稱和導演。類似的原則用于檢查Song執行個體,并在數組中找到Song時列印适當的描述(包括名稱和表演者)。

注意: 轉換實際上不會修改執行個體或更改其值。執行個體保持不變; 它被簡單地處理和通路為它的類型的執行個體。

Any和AnyObject類型轉換

Swift提供了兩種特殊類型來處理非特定類型:

  • Any 可以表示任何類型的執行個體,包括函數類型。
  • AnyObject 可以表示任何類類型的執行個體。

使用Any和AnyObject隻有當明确需要提供的的行為以及功能。最好轉化為在代碼中使用的類型。

以下是使用Any不同類型混合的示例,包括函數類型和非類類型。該示例建立一個名為things的Any數組,該數組可以存儲任何類型的值:

var things = [Any]()

things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
           

things數組包含兩個Int值,兩個Double值,一個String值,一個(Double, Double)類型的元組,電影“Ghostbusters”,以及一個帶有String值并傳回另一個String值的閉包表達式。

要把Any或者AnyObject類型轉換為特定類型的常量或變量,可以使用一個is或as模式作為switch語句的情況。下面的示例周遊things數組中的項目,并使用switch語句查詢每個項目的類型。一些switch語句的情況将它們的比對值綁定到指定類型的常量,以使其值可以列印:

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}
           

注意: Any類型表示任何類型的值,包括可選類型。如果使用期望值為可選類型,Swift會向發出警告Any。如果确實需要使用可選值作為Any值,則可以使用as運算符顯式地将可選值轉換為Any,如下所示。

let optionalNumber: Int? = 3
things.append(optionalNumber)// 有警告
things.append(optionalNumber as Any)// 沒有警告