shiokara さんの回答とそれに寄せられていた garubor01 さんの補足情報を拝見しました。ある型を別の型として扱おうとしたとき、Swift 通称 変換イニシャライザー
(今回であれば Narrowing Type Conversion)と呼ばれる、別の型から自分自身の型に変換するためのイニシャライザー** を型ごとに実装することを推奨していたりするので、一般的には shirokara さんの示されたコードがお行儀の良いコードになるように感じます。
その上で、garubor01 さんの補足が興味深く思えましたので、Swift でそれっぽいものを書くとしたらどんなふうになるか試してみました。そうしたところ、最初のご質問で触れられていた Codable
を使うと比較的それっぽく、簡単なコードで実現できた様子なので紹介させていただきますね。
□ キャストに対応していることを示すプロトコルを用意する
Swift では、型を跨いで何かしらのルールを適用するためには(今回であればキャスト可能とするためには)プロトコルという仕組みが不可欠になりがちなので、今回は Castable
というプロトコルを用意してみました。
swift
1protocol Castable : Codable {
2}
変換には Codable
の仕組みを使おうと思うため、あらかじめそれを継承したプロトコルにしておきます。
なお、今回は Codable
というプロトコルにさえ適用された型であれば大丈夫なので、フリーな関数(グローバル関数)でキャストするならなくても実現可能ですけど、Swift らしくよりスマートに見せるために用意しています。
□ キャスト関数を用意しておく
今回、Castable
プロトコルに準拠した型であれば、どんな型でも別の型へすぐにキャストできる仕組みを提供したいので、Castable
プロトコル自身に casting
というメソッドをディフォルト実装しておきます。
swift
1extension Castable {
2
3 func casting<R: Castable>() -> R {
4
5 let data = try! JSONEncoder().encode(self)
6 return try! JSONDecoder().decode(R.self, from: data)
7 }
8}
ここで使用する JSONDecoder
や JSONEncoder
は、Swift のコアライブラリー Foundation
をインポートすると使える機能で、これが今回にはとても都合の良いことに、オプショナル型のプロパティーであれば、対象のキーがある時には値を設定し、対象のキーがない時は nil
として扱ってくれます。
そのため、上記のようにとてもシンプルにコードを書くことができました。
□ キャストしてみる
これで準備は整ったはずなので、次のように Castable
に対応した型を2つ作って実験してみます。
swift
1struct A : Castable {
2
3 var a: Int?
4 var b: Int?
5}
6
7struct B : Castable {
8
9 var a: Int?
10 var b: Int?
11 var c: Int?
12}
実験1
swift
1var a = A()
2a.a = 1
3a.b = 2
4
5let b = a.casting() as B
6
7dump(b.a) // 1
8dump(b.b) // 2
9dump(b.c) // nil
実験2
swift
1var b = B()
2b.a = 1
3b.b = 2
4b.c = 3
5
6let a = b.casting() as A
7
8dump(a.a) // 1
9dump(a.b) // 2
所感
キャストの際に casting() as X
みたいに書かないといけないですけれど、それでも Swift としては最短な感じで表現できているのと、型ごとの事前準備は Casatable
プロトコルに準拠させるだけで済むので、だいぶ善戦した感じのコードになっているような気がします。
ただ、冒頭でも触れたように、Swift のガイドライン的にはあまり行儀の良くないコードのようにも思えますので、誰かと共同で制作するプロジェクトでは使用を控えておくのが無難かもしれないです。
そう思いはしたものの、改めて眺めてみると今回に自分が挙げた例でもそこまで誤解を招くようなコードになっていない気もしてきたので、もしこのコードに携わる誰もが、ここでいうキャストの動作が今回の質問で記されている動きをするものという認識をズレなく持っている場合であれば使っても大丈夫かもしれない気もしてきました。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/09/05 17:12