Quantumleap
1080 words
5 minutes
Swiftでの型のキャストを理解せよ
2021-07-28

キャストとは#

キャストとは型を変換すること。相互変換できる型だったり、できない型だったりがある。

キャストの方法はいろいろあるのですが、自分の理解が不十分だったため今回 Playground を使って理解を深めていこうと思います。

イニシャライザ#

いくつかの型はイニシャライザを使ってキャストができます。

Int to String#

let intValue: Int = 10 // Int
let stringValue: String = String(intValue) // Int to String

print(stringValue, type(of: stringValue)) // -> 10 String

この方法のキャストは必ずStringが返ってくるのでそのままStringで受けることができます。

String to Int#

一方、StringからIntへのキャストは必ず成功するとは限りません。

当たり前ですが、整数型に変換できない文字列が存在するためです。よって、以下のコードは書けません。

let stringValue: String = "10" // String
let intValue: Int = Int(stringValue) // Value of optional type 'Int?' must be unwrapped to a value of type 'Int'

ここで大事なのは仮に変換可能な文字列がstringValueに代入されていたとしてもコンパイラはこのエラーを出力するということです。つまり、実行時エラーではなくてコンパイルエラーとなるわけです。

Int()を使ったStringからのキャストはOptional<Int>が返り、変換不可能な場合はnilが入っています。

Optional をそのまま利用する場合#

let stringValue: String = "10" // String
let intValue: Int? = Int(stringValue) // String to Int

print(intValue, type(of: intValue)) // -> Optional(10) Optional<Int>

強制アンラップする場合#

let stringValue: String = "10" // String
let intValue: Int = Int(stringValue)! // String to Int

print(intValue, type(of: intValue)) // ->10 Int

この方法はstringValueにキャストできない文字列が含まれていると実行時にクラッシュします。

デフォルト値を利用する場合#

let stringValue: String = "10" // String
let intValue: Int = Int(stringValue) ?? 0 // String to Int

print(intValue, type(of: intValue)) // ->10 Int

??を使い、後ろにデフォルト値を設定すればnilが返ってきた場合にその値が利用されます。

クラッシュはしないのですが、逆に言えばクラッシュしないのでどこでエラーが発生しているのかがわかりにくくなります。

エラーを返す場合#

let stringValue: String = "10" // Int
guard let intValue: Int = Int(stringValue) else { throw fatalError() }

print(intValue, type(of: intValue)) // -> 10 Int

guard let else {}を利用すれば安全にオプショナルを外し、エラーを返すことができます。

任意の処理を行う場合#

let stringValue: String = "10" // Int
if let intValue: Int = Int(stringValue) {
    // nilでないとき
    print(intValue, type(of: intValue)) // -> 10 Int
} else {
    // nilのとき
    print("Input value does not cast as Int")
}

分岐をしたいときにif intValue == nilのように書くこともできますが、こちらの方がスマートで良いと思います。

個人的にですが、nilというのを単純に比較演算子で比較したくないですね。isNilみたいなメソッドがほしいですね。

Any to String#

文字列型は基本的にどんな型がきてもキャストすることができます。

let intValue: Int = 10
let optionalIntValue: Int? = 10

print(String(intValue))
print(String(optionalIntValue)) // Value of optional type 'Int?' must be unwrapped to a value of type 'Int'

ただしString()ではオプショナルや構造体、クラスなどはキャストできません。

let optionalIntValue: Int? = 10

print(String(describing: optionalIntValue)) // -> Optional(10)

そこでString(describing: )を使います。これは基本的に何でもキャストできます。

struct UserInfo {
    var userId: Int = 0
    var userName: String = "tkgling"
}

print(String(describing: UserInfo())) // -> UserInfo(userId: 0, userName: "tkgling")

このように構造体も文字列型にキャストできます。

キャスト演算子#

Swift にはasas?as!の三つのキャスト演算子があります。

これの使い方がよくわかっていなかったので今回は学習しました。

また、キャストにはダウンキャストとアップキャストの二つがあります。その違いについてもまずは理解しましょう。

ダウンキャスト#

アップキャスト#

as#

キャストして失敗したらエラーが発生します。

let intValue: Int = 10
let stringValue: String = intValue as String

print(stringValue) // Cannot convert value of type 'Int' to type 'String' in coercion

as?#

キャストして失敗したらnilが返ります。

as!#

キャストして失敗したらエラーが発生し、そのままクラッシュします。

Swiftでの型のキャストを理解せよ
https://fuwari.vercel.app/posts/2021/07/swiftcast/
Author
tkgling
Published at
2021-07-28