概要
- swiftの型変換を行うas、型チェックを行うisの挙動のメモ
- 検証環境Xcode6Beta2
- playgroundでas/isを使うとすごく落ちやすいので使わないほうが吉
- ドキュメントは https://itunes.apple.com/jp/book/swift-programming-language/id881256329?mt=11 「Type Casting」で検索すると出てくる
isとはなにか?
型チェック
インスタンスに対してチェックを行い、True/Falseを返す
[インスタンス] is [チェックしたいクラス]
簡単な使い方
is
let array : Any[] = ["text", 10, 20, "30"]
for obj in array {
if obj is Int {
println("\(obj) is Int")
}else if obj is String{
println("\(obj) is String")
}
}
// text is String
// 10 is Int
// 20 is Int
// 30 is String
asとはなにか?
型変換
インスタンスをダウンキャストする
数字=>文字列のような変換をするものではない
共通するベースクラスにアップキャストされたインスタンスをダウンキャストする場合に使用する(わかりにくいのであとで実例で詳細に)。
as?とすると、変換が失敗した場合はnilを返すようにできる
つけないと、失敗した場合ランタイムエラーになる
// 強制的にダウンキャストする。失敗した場合はランタイムエラー
let [ダウンキャストされたインスタンス] = [インスタンス] as [ダウンキャスト先のクラス]
// ダウンキャスを試みて、失敗した場合はnilを返す
let [ダウンキャストされたインスタンス(失敗すればnilが入る)] = [インスタンス] as? [ダウンキャスト先のクラス]
簡単な使い方
as
// Animalクラスとそれを継承したDog/Catクラスを用意する
class Animal{
}
class Cat: Animal{
let say = "nya-"
}
class Dog: Animal{
let say = "wan wan"
}
// animalsの型はDogとCatの共通ベースクラスにアップキャストされたAnimal配列になる
// let animals = [Dog(), Cat()] でも型推論で同じ動作になる
let animals : Animal[] = [Dog(), Cat()]
// animalは配列animalsの要素なので、型はAnimalとして扱われる
for animal in animals {
// Animal型にアップキャストされているanimalをCatでダウンキャストするように試みる
if let cat = animal as? Cat {
// ダウンキャストされてCatのインスタンスとして扱えるので、sayプロパティが使える
println("Cat say \(cat.say)")
// 同様にDogにダウンキャストを試みる
} else if let dog = animal as? Dog {
println("Dog say \(dog.say)")
}
}
// Dog say wan wan
// Cat say nya-
いろんな書き方を試す
IntとDoubleはasでキャストできない
// 10.0や"string"といったコンビニエンスコンストラクタを使用している場合型が未確定で何かしらの型にアップキャストされている状態なので、asでキャストできるという解釈でいいのかな?
var string01 = "string" as String
var int01 = 10.0 as Int
// int01はIntが確定しているので、Doubleにキャストできない。エラーになる
// var Double01 = int01 as Double
// ?をつければ文法上は問題ないはずだが、Xcodeに怒られてエラーになる
// var Double02 = int01 as? Double
// 'is' test is always true trueであることが明確な場合はエラーになる
// var bool : Bool = 1000 is Int
println(string01)
println(int01)
// string
// 10
isだけでいけるパターンとasでなければいけないパターン
共通で使用するクラス
class Animal{
var cry:String?
init(){
}
}
class Cat: Animal{
init(){
super.init()
cry = "nya-"
}
}
class Dog: Animal{
init(){
super.init()
cry = "wan wan"
}
func walk () -> String {
return "Love walk"
}
}
isを使う
let animals = [Dog(), Cat(), Animal()]
for animal in animals {
if animal is Cat {
println("Cat say \(animal.cry)")
} else if animal is Dog {
println("Dog say \(animal.cry)")
}
}
// Dog say wan wan
// Cat say nya-
Animal/Cat/Dogすべてのクラスでcryプロパティがあるので、Animalにアップキャストされた状態でもエラーにならない
エラーになるパターン
let animals = [Dog(), Cat(), Animal()]
for animal in animals {
if animal is Cat {
println("Cat say \(animal.cry)")
} else if animal is Dog {
// animalインスタンスがDogクラスであることは確定しているが、あくまでanimalはAnimalクラスにアップキャストされているためAnimalクラスとして扱われる
// したがってwalkメソッドはないためエラーになる
println("Dog is \(animal.walk())")
}
}
上の書き方をエラーにならないように
let animals = [Dog(), Cat(), Animal()]
for animal in animals {
if animal is Cat {
println("Cat say \(animal.cry)")
} else if animal is Dog {
// isでDogクラスであることは確定したので、as?でなくasでもエラーにならない
let dog = animal as Dog
println("Dog is \(dog.walk())")
}
}
// Dog is Love walk
// Cat say nya-
雑多なデータを混ぜる
let string = "sample"
// animalsはstringが入ったために、AnyObject配列になる
let animals : AnyObject[] = [Dog(), Cat(), Animal(), string]
// animalはAnyObject型として扱われる
for animal : AnyObject in animals {
if let cat = animal as? Cat {
println("Cat say \(cat.cry)")
} else if let dog = animal as? Dog {
println("Dog is \(dog.walk())")
} else if animal as? String{
println("Not Animal. it is String => \(animal)")
}
}
// Dog is Love walk
// Cat say nya-
// Not Animal. it is String => sample
まとめ
- isはチェックをするだけ
- asはアップキャストされたものをダウンキャストするだけ
- Xcodeは文法的に問題なくても、不必要なチェックやキャストをエラーにすることがある(正式版ではwarningになったりするかも?)