天天看點

Swift學習筆記(十五)類型轉換和類型嵌套

類型檢查在 Swift 中使用is和 as操作符實作。這兩個操作符提供了一種簡單達意的方式去檢查值的類型或者轉換它的類型。

定義一個類層次作為例子

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
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)
    }
}
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")
]
// the type of "library" is inferred to be [MediaItem]
           

library的儲存的是Movie和Song的執行個體,若你疊代它,則取出的會是其父類MediaItem類型,而不是Movie和Song類型。為了讓它們作為它們本來的類型工作,你需要檢查它們的類型或者向下轉換它們的類型到其它類型。

通過操作符is來檢查一個執行個體是否屬于特定的子類型,傳回true則屬于

var movieCount = 0
var songCount = 0
for item in library {
    if item is Movie {
        ++movieCount
    } else if item is Song {
        ++songCount
    }
}
println("Media library contains \(movieCount) movies and \(songCount) songs")
// prints "Media library contains 2 movies and 3 songs"
           

向下轉型

通過類型檢查操作符as可向下轉型.但是由于向下轉型可能會失敗

故有兩種形式①可選形式as? ②強制形式as!

當你确定轉型一定會成功的時候使用as!

for item in library {
    //由于item是一個MediaItem類型的執行個體,它可能是一個Movie,故使用as?
    if let movie = item as? Movie {
        println("Movie: '\(movie.name)', dir. \(movie.director)")
    } else if let song = item as? Song {
        println("Song: '\(song.name)', by \(song.artist)")
    }
}
           

Any和AnyObject類型檢查

Swift為不确定類型提供了兩種特殊類型别名:

①AnyObject可以代表任何class類型的執行個體。

②Any可以表示任何類型,除了方法類型(function types)。

注意:隻有當你明确的需要它的行為和功能時才使用Any和AnyObject。在你的代碼裡使用你期望的明确的類型總是更好的。

AnyObject類型

let someObjects: [AnyObject] = [
    Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
    Movie(name: "Moon", director: "Duncan Jones"),
    Movie(name: "Alien", director: "Ridley Scott")
]
           

由于這個數組中隻包含Movie執行個體,是以你可以直接使用as!來進行強制解包

for object in someObjects {
    let movie = object as! Movie
}
           

或者直接将[AnyObject]強轉成[Movie]

for movie in someObjects as! [Movie] {
    println("Movie: '\(movie.name)', dir. \(movie.director)")
}
           

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中包含2個Int,2個Double,1個String,1個元組 (Double, Double),一個Movie對象

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

// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called 'Ghostbusters', dir. Ivan Reitman
           

類型嵌套

一個類型中嵌套另一個類型,将需要嵌套的類型的定義寫在被嵌套類型的區域{}内,而且可以根據需要定義多級嵌套

struct BlackjackCard {
    // 嵌套定義枚舉型Suit
    enum Suit: Character {
        case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣"
    }
    // 嵌套定義枚舉型Rank
    enum Rank: Int {
        case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten
        case Jack, Queen, King, Ace
        struct Values {
            let first: Int, second: Int?
        }
        var values: Values {
            switch self {
            case .Ace:
                return Values(first: 1, second: 11)
            case .Jack, .Queen, .King:
                return Values(first: 10, second: nil)
            default:
                return Values(first: self.toRaw(), second: nil)
            }
        }
    }
    // BlackjackCard 的屬性和方法
    let rank: Rank, suit: Suit
    var description: String {
        var output = "suit is \(suit.toRaw()),"
        output += " value is \(rank.values.first)"
        if let second = rank.values.second {
            output += " or \(second)"
        }
        return output
    }
}
           

枚舉型的 Suit 用來描述撲克牌的四種花色,并分别用一個 Character 類型的值代表花色符号。

枚舉型的Rank用來描述撲克牌從Ace~10,J,Q,K,13張牌,并分别用一個Int類型的值表示牌的面值。(這個Int類型的值不适用于Ace,J,Q,K的牌)。

舉型Rank在自己内部定義了一個嵌套結構體Values。這個結構體包含兩個變量,隻有Ace有兩個數值,其餘牌都隻有一個數值。結構體Values中定義的兩個屬性:first, 為Int ;   second, 為 Int?, 或 “optional Int”

Rank定義了一個計算屬性values,這個計算屬性會根據牌的面值,用适當的數值去初始化Values執行個體,并指派給values。對于J,Q,K,Ace會使用特殊數值,對于數字面值的牌使用Int類型的值。

BlackjackCard結構體自身有兩個屬性—rank與suit,也同樣定義了一個計算屬性description,description屬性用rank和suit的中内容來建構對這張撲克牌名字和數值的描述,并用可選類型second來檢查是否存在第二個值,若存在,則在原有的描述中增加對第二數值的描述。

因為BlackjackCard是一個沒有自定義構造函數的結構體,在Memberwise Initializers for Structure Types中知道結構體有預設的成員構造函數,是以你可以用預設的initializer去初始化新的常量theAceOfSpades:

let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
println("theAceOfSpades: \(theAceOfSpades.description)")
// 列印出 "theAceOfSpades: suit is ♠, value is 1 or 11"
           

盡管Rank和Suit嵌套在BlackjackCard中,但仍可被引用,是以在初始化執行個體時能夠通過枚舉類型中的成員名稱單獨引用。在上面的例子中description屬性能正确得輸出對Ace牌有1和11兩個值。

類型嵌套的引用

在外部對嵌套類型的引用,以被嵌套類型的名字為字首,加上所要引用的屬性名:

let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw()
// 紅心的符号 為 "♡"