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
のアップキャストできるので、型の一致性が保たれます。
AnyObject
と Any
異なる型の値を配列・辞書に混在させたい場合には Any
を利用することができます。
var array : Any[] = ["1",2,3]
この時、要素として指定できるのは関数以外の全ての型のようです。
AnyObject
は若干適用範囲が狭く、クラスのインスタンスに対して利用できます。したがって、AnyObject
で Int
や Bool
などのプリミティブな型は指定できません。
先ほどのサンプルコード、
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")
}
}
is
は Int
などのプリミティブな型に対しても動作します。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
}
}
型変換 as
と as?
共通するベースクラスにアップキャストされたインスタンスを、ダウンキャストするには 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 構文での as
と is
switch
文の case
に is
と as
を使って条件分岐を実現できます。
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
などを使って定数に代入もできます。
switch obj {
case let bar as Bar: // if let bar = obj as? Bar と同じ
// .... この中で bar がローカルな定数として利用できる
}
if
文で as?
を使うような場面でも switch
文の case
では as
を利用しています。
switch
内のコンテクストで case
での強制的な型変換はエラーにならないようです (そもそもマッチしない)。