Swiftに関するメモ その7 - 型変換


* [
Swiftに関するメモ - 一覧](http://qiita.com/takkyun/items/f34abf857deed808c610)

https://developer.apple.com/swift/ より

[The Swift Programming Language (iBooks Store)](https://itunes.apple.com/us/book/the-swift-programming-language/id881256329?mt=11)をざっくり目を通してつらつらと。

少しサボってしまいました。

# 配列・辞書における型の安全性 (Type safe)

Swift では、基本的に配列・辞書 (ディクショナリ) が、型安全 (type safe) で動作します。

例えば、以下のようなコード、

```swift
var array = ["1",2,3]
```

[The Swift Programming Language (iBooks Store)](https://itunes.apple.com/us/book/the-swift-programming-language/id881256329?mt=11) の Collection Type の冒頭にある通り、配列の全て要素が一致しないため、エラーになります。実行前のコンパイル時点でエラーになるようです。

---

> 配列の全て要素が一致しないため、エラーになります。実行前のコンパイル時点でエラーになるようです。

と記載しましたが、型推論によってコンパイルエラーにはならないそうです。

---

同様に以下のコードもエラーになります。

```swift
class Fizz {
}
class Buzz {
}
class Foo {
}
class Bar {
}
var array = [Fizz(),Buzz(),Foo(),Bar(),Bar()]
```

Swift では標準的なベースクラスがないとされていています。

上記の例だとそれぞれのクラスが独立していて、上記のサンプルコードでは、型の一致性が保証できないためです。

---

@takabosoft さんがコメントでご指摘されたように `import Cocoa` を宣言したりすると、コンパイラが型推論できる範囲が広がり、上記コードでもエラーにならなくなる場合があるようです。

Swift の配列に関しての動作は、理解しづらい代入時の動作なども含めて、今後変わってくる可能性があると個人的には思っています。

---

以下ようなコードだと動作します。

```swift
class BaseClass {
}
class Fizz : BaseClass {
}
class Buzz : BaseClass {
}
class Foo : BaseClass {
}
class Bar : Foo {
}
var array = [Fizz(),Buzz(),Foo(),Bar(),Bar()]
```

配列の全て要素がベースクラス `BaseClass` のアップキャストできるので、型の一致性が保たれます。

# `AnyObject` と `Any`

異なる型の値を配列・辞書に混在させたい場合には `Any` を利用することができます。

```swift
var array : Any[] = ["1",2,3]
```

この時、要素として指定できるのは関数以外の全ての型のようです。

`AnyObject` は若干適用範囲が狭く、クラスのインスタンスに対して利用できます。したがって、`AnyObject` で `Int` や `Bool` などのプリミティブな型は指定できません。

先ほどのサンプルコード、

```swift
class Fizz {
}
class Buzz {
}
class Foo {
}
class Bar {
}
var array = [Fizz(),Buzz(),Foo(),Bar(),Bar()]
```

はエラーになりましたが、`AnyObject` を明示すればエラーにはなりません。

```swift
class Fizz {
}
class Buzz {
}
class Foo {
}
class Bar {
}
var array : AnyObject[] = [Fizz(),Buzz(),Foo(),Bar(),Bar()]
```

# 型チェック `is`

`is` を使うと変数・定数に対しての型チェックを行うことができます。

```swift
class BaseClass {
}
class Fizz : BaseClass {
}
class Buzz : BaseClass {
}
class Foo : BaseClass {
}
class Bar : Foo {
}
var array = [Fizz(),Buzz(),Foo(),Bar(),Bar()]
for obj in array {
  if obj is Bar {
    println("obj is Bar")
  }
}
```

`is` は `Int` などのプリミティブな型に対しても動作します。Objective-C の `isKindOfClass:` とはこの点で異なります。

```swift
var array : Any[] = ["1",2,3]
for obj in array {
  if obj is Int {
    println("\(obj) is Int") // 2 is Int & 3 is Int
  }
}
```

# 型変換 `as` と `as?`

共通するベースクラスにアップキャストされたインスタンスを、ダウンキャストするには `as` が利用できます。この時、`?` を付けたオプショナル形式を利用することができます。

```swift
let bar = obj as Bar // 定数 bar に対して obj から Bar でダウンキャストしたインスタンスを代入する
```

`?` をつけない場合は強制的な型変換になります。この時、変換の対象となる変数・定数がその型に変換できない場合、エラーになります。

それに対して `?` をつけると、変換後の定数・変数に任意型 (optinal) を取るような動作になり、変換対象がその型に変換できない場合には `nil` が入ります。

それを利用すると、

```swift
for obj in array {
  if obj is Bar {
    let bar = obj as Bar
    // bar について何かする
  }
}
```

というコードを

```swift
for obj in array {
  if let bar = obj as? Bar {
    // bar について何かする
  }
}
```

のように記述できます。

# switch 構文での `as` と `is`

`switch` 文の `case` に `is` と `as` を使って条件分岐を実現できます。

```swift
switch obj {
  case is Bar: // obj が Bar だったら => if obj is Bar と同じ
    // .... 省略
  case 0 as Int: // obj が整数の 0 の場合 => if obj as? Int == 0 と同じような意味
    // .... 省略
  case 0 as Double: // obj が Double の 0.0 の場合 => if obj as? Double == 0.0 と同じような意味
    // .... 省略
}
```

`case` で `let` などを使って定数に代入もできます。

```swift
switch obj {
  case let bar as Bar: // if let bar = obj as? Bar と同じ
    // .... この中で bar がローカルな定数として利用できる
}
```

`if` 文で `as?` を使うような場面でも `switch` 文の `case` では `as` を利用しています。

`switch` 内のコンテクストで `case` での強制的な型変換はエラーにならないようです (そもそもマッチしない)。

参考 → [as と is の入った switch 文を if 文で記述してみる](http://qiita.com/takkyun/items/d5497443d10ddec67db4)