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

  • 66
    Like
  • 3
    Comment
More than 1 year has passed since last update.

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

The Swift Programming Language (iBooks Store)をざっくり目を通してつらつらと。

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

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

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

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

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

The Swift Programming Language (iBooks Store) の Collection Type の冒頭にある通り、配列の全て要素が一致しないため、エラーになります。実行前のコンパイル時点でエラーになるようです。


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

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


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

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

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

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


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

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


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

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

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

AnyObjectAny

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

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

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

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

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

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

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

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

型チェック is

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

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")
  }
}

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

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

型変換 asas?

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

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

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

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

それを利用すると、

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

というコードを

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

のように記述できます。

switch 構文での asis

switch 文の caseisas を使って条件分岐を実現できます。

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 と同じような意味
    // .... 省略
}

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

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 文で記述してみる