キャスト とは
キャストとは、値の型を確認し、可能であれば別の型として扱う操作のことです。
型のキャストは、階層関係にある型どうしで行います。
プログラミングにおける階層関係とは、上の階層にあるものほど抽象的で、
下の階層にあるほど具体的な内容になります。
例えば、一番上の階層に料理があったとすると、
料理
|ーイタリアン
| |ーパスタ
| | |ーペペロンチーノ
| | |ーミートソース
| |
| |ーピザ
| |ーマルゲリータ
| |ーペスカトーレ
|
|ー和食
|ーうどん
| |ー讃岐うどん
| |ー稲庭うどん
|
|ー蕎麦
といった感じに、下の階層に行けば行くほど具体的なものになります。
そして、階層関係にあるものどうしをキャストすることができます。
なので、うどんは料理として扱うことができますし、
イタリアンをミートソースとして扱うこともできます。
しかし、階層関係にないものはキャストできません。
先ほどの例でいうと、
稲庭うどんをピザとして扱うことは階層が違うのでできないということです。
アップキャスト
アップキャストは、階層関係がある型どうしにおいて、
階層の下位となる具体的な型を階層の上位の抽象的な型に変換することができます。
アップキャストを行う際にはas演算子を使用します。
as演算子の左側には下位の型を指定し、右側には上位の型を指定します。
階層関係にない型どうしはコンパイルエラーになります。
let hello: String = "こんにちは" // String
let a = hello as Any // こんにちは
let b = hello as Int // コンパイルエラー
Any型は全ての型が暗黙的に準拠しているプロトコルなので、
全ての型はAny型にアップキャストすることが可能です。
また、重要なのが、
let hello: String = "こんにちは"
にて、定数helloはString型なので、
String型に備わっている機能を使用することができますが、
let a = hello as Any
の定数aはAny型として扱われるので、
String型に備わっている機能を使用することができません。
ダウンキャスト
ダウンキャストとは、階層関係のある型どうしにおいて、
階層の上位となる抽象的な型を下位の具体的な型として扱う操作になります。
アップキャストにおいては、具体的な型を抽象的な型として扱うので、
コンパイル可能な(= 階層関係にある)アップキャストは常に成功する操作でした。
しかし、ダウンキャストはコンパイル可能でも失敗する可能性があります。
ダウンキャストに使用する演算子は、as!演算子と、as?演算子の2種類があります。
また!と?だよ・・・。ややこしい!と思うかもしれませんが違いは簡単で、
それぞれの演算子の違いは失敗時の動作にあります。
as?演算子
as?演算子は失敗した時にnilを返します。
as?演算子の結果は、キャスト先の型の値かnilになる可能性があるため、
Optional<Wrapped>型になります。
let a = 1 as Any
let b = a as? Int // Optional(1)
let c = a as? String // nil
Any型にアップキャストする前の型がInt型であるため、
Int型にダウンキャストする際はキャストに成功します。
しかし、String型にダウンキャストする場合は、
Int型とString型で階層関係にないのでキャストが失敗しnilが渡されます。
as!演算子
as!演算子は強制キャストとも言われるダウンキャストです。
as?演算子と違い、失敗した際にはnilを返さずにエラーが発生します。
let a = 1 as Any
let b = a as! Int
let c = a as! String // 実行時エラー
エラー内容:error: Execution was interrupted, reason: signal SIGABRT. The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
ダウンキャストに失敗した場合はプログラムの実行が継続されないので、
Optional<Wrapped>型ではなくWrapped型になります。
つまり、通常のInt型やString型と同じ扱いです。
let a = 1 as Any
let b = a as? Int // Optional(1)
let c = 1 + b // コンパイルエラー
let a = 1 as Any
let b = a as! Int // 1
let c = 1 + b // 2
as?演算子は、値がない時にnilを返すので実行時エラーが発生しないため比較的に安全ですが、
nilが関わる以上は、オプショナルバインディングや??演算子などの
nilを考慮した処理を記述する必要があるかもしれません。
一方、as!演算子は実行時エラーは発生しますが、nilを考慮する必要がありません。
失敗しないことが保証されている場合はas!演算子でいいと思いますが、
基本的にはas?演算子を使用した方が安全かもしれませんね。
以上、最後までご覧いただきありがとうございました。