キャストについて調べていたのですが、疑問点があり、質問させて貰いました。
let test2: String = "sample" print("type Of test2: (type(of:test2))") let test1: Any = test2 as Any print("type Of test1: (type(of:test1))")
こちらなのですが、printの結果はいずれも「String」型になってしまいます。
アップキャストが行われているのだとしたら、test1はanyになるのではないかと思うのですが、何故String型のままなのでしょうか...?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
公式ドキュメントに書いてあります
公式ドキュメントのここが、わからないは、質問に普通に追記でいいと思いますよ。
むしろ、コメントは検索対象にならないので、せっかくの書いてもらったのに質問が埋もれます。
「このtype(of:)関数を使用して、特に動的タイプが静的タイプと異なる場合に、値の動的タイプを見つけることができます。値の静的な型は、既知のコンパイル時の値の型です。値の動的な型は、実行時の値の実際の型であり、具体的な型のサブタイプになる場合があります。」
まず、値の静的タイプの説明として、「既知のコンパイル時の値の型です。」とありますが、これが何を示しているのかが分からないです。そして、動的なタイプの説明として、「具体的な型のサブタイプ」とありますが、こちらの意味もよく分からないです
静的タイプ(既知のコンパイル時の型)について
「既知のコンパイル時の値の型です。」
ソースコードに定義されている型のことです。
動的タイプは実行時の実態の型です。
swift
1func printInfo(_ value: Any) { 2 let t = type(of: value) 3 print("'(value)' of type '(t)'") 4} 5 6let count: Int = 5 7printInfo(count) 8// '5' of type 'Int'
countは、静的タイプはIntです。
動的なタイプは5なのでIntです。
このcount変数printInfo関数へ渡していますが
printInfo関数内での仮引数、value の静的タイプはAnyです。
そして、type(of:)で取得した動的タイプはIntです。
質問者さんのコードでいうと
Swift
1let test2: String = "sample" 2print("type Of test2: (type(of:test2))") 3let test1: Any = test2 as Any 4print("type Of test1: (type(of:test1))")
test2のStringと、test1のAnyの部分が静的タイプです。
「具体的な型のサブタイプ」
端的にいっちゃうと、ダウンキャストしたものです。
関数の引数は汎用的にするためAnyとまではいかないですが、親の型を定義(静的タイプ)とすることが多いと思います。
元の型でなく、実際のインスタンスである、継承した型(サブタイプ)を取得するということです。
大元の質問に戻ると
test1はanyになるのではないかと思うのですが
Anyになると思っていることそのものが勘違いで、本来の型であるStringを取得することが正しいです。
静的?動的?って、混乱してるかもしれませんが、単純な例でいうと。
ソースでStringって定義したから実態がStringになるわけじゃないんです。
以下は、Stringっていう文字がでてこないですが、"sample"の実態はStringなので、type(of:)の結果はStringです。
Swift
1let test1: Any = "sample" 2print("type Of test1: (type(of:test1))")
投稿2019/12/02 12:39
編集2019/12/03 04:09総合スコア4826
0
ベストアンサー
PHPでは、
PHP
1$foo = "5"; // $fooは文字列 2$foo = (int) $foo; // $fooは整数に変わる
というように変数の型が動的に変化する上、実体(インスタンス)も変化するかのように動作します。aae_11さんはおそらくこういう動作を想定しているのではないかと思います。
Swiftではこのような事はできません。Swiftのキャストは、PHPのキャストような型変換機能ではありません。let test1: Any = test2 as Any
という文はtest2
をAny
型に変換してtest1
に代入しているのではないのです。
まず、Swiftで型を変えるには、別の型の実体を新たに作り出す必要があります。下記のint1
はstring1
を元に、Int
の実体を新たに作る事で型変換を行っています。キャストしているのではありません。int2のようにキャストでやろうとすると、エラーになります。Float
とDouble
の間でさえ、キャストでは変換できません。
Swift
1let string1 = "5" // string1はString型の"5"になる。 2let int1 = Int(aString)! // int1はInt型の5になる。 3let int2 = string1 as Int // エラー "Cannot convert value of type 'String' to type 'Int' in coercion"
それではSwiftの型キャストとは何でしょう。説明を抜粋します。
Type casting is a way to check the type of an instance, or to treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy.
Type casting in Swift is implemented with the is and as operators. These two operators provide a simple and expressive way to check the type of a value or cast a value to a different type.
抜粋:: Apple Inc. “The Swift Programming Language (Swift 5.1)”。 Apple Books https://books.apple.com/jp/book/the-swift-programming-language-swift-5-1/id881256329
意訳:型キャストは、実体の型を調べたり、実体のクラス階層のなかでスーパークラスやサブクラスとして扱うための機能です。is
とas
演算子で実装されます。
Swiftのキャストは、スーパークラスやサブクラスの間でしか使えません。違う型に変換しようとするとエラーになります。また、型変換の機能はそもそもありません。
Any
型はとても特殊で、どの型でもAny
と相互にキャストできます。
let test1: Any = test2 as Any
という文はtest2
をAny
型とみなして、Any
型であるtest1
に代入しています。実体はなんら変化しません。その実体を参照している変数の型が違うだけです。
Swiftは暗黙のキャストをしてくれるので、明示的に指定しなければならないのはAny
がからむときくらいですが、たとえばUserDefault
ではよく使います。
swift
1let a = UserDefaults.standard.integer(forKey: "storedIntValue") 2let b = UserDefaults.standard.object(forKey: "storedIntValue") as! Int
保存されている"storedIntValue"キーの値がInt
型なら、a
とb
は同じになります。しかし、Int
型じゃない場合、a
にはなんらかのInt
の値が得られますが、b
はキャストに失敗するのでクラッシュします。
なお、危険なのでAny
の使用はライブラリが要求する場合のみに留め、自分で使うのはできるだけ避けるのが定石です。型が提供する利点を破壊する存在なので。
投稿2019/12/03 08:22
総合スコア803
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/12/03 11:40
2019/12/03 12:06
0
まず、変数(または定数)の型と、その変数に入っている値の型は一致しているとは限りません。
例えばlet a: Any = "text"
であれば a
の型はAnyですが、実際に入っている値の型はstring
です。
で、アップキャストはキャスト対象をスーパークラスとして使えるようにするだけで、値自体の何かしらを書き換えるわけではありません。
なので、
let test1: Any = test2 as Any
は
let test1: Any = test2
と書いてるのと一緒です。
次にtype()についてですが、
変数自体の型はソースコードを見ればわかる(=静的)ので調べる必要がありません、例えばstring
型の変数s
の変数の型はstring
です。
変数に入っている値の型はソースコードを見ても確定するとは限りません。実行時に決まります。(=動的)
type()関数は動的なほうの型を調べる関数です。
おまけ:キャストについてのリファレンス
The as operator performs a cast when it is known at compile time that the cast always succeeds, such as upcasting or bridging. Upcasting lets you use an expression as an instance of its type’s supertype, without using an intermediate variable. The following approaches are equivalent:
func f(_ any: Any) { print("Function for Any") } func f(_ int: Int) { print("Function for Int") } let x = 10 f(x) // Prints "Function for Int" let y: Any = x f(y) // Prints "Function for Any" f(x as Any) // Prints "Function for Any"
as
演算子はコンパイル時には成功するとわかっているキャスト(例えばアップキャスト)に使う- (アップキャストを使うと)中間変数を使わずに、式(expression)をスーパータイプとして使える
投稿2019/12/03 05:52
編集2019/12/03 05:53総合スコア13551
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/12/03 07:00
2019/12/03 07:06
0
コンパイラが認識する型が変わるだけといえば分かりますか?
追記
(注意:以下の説明で用いているラベルという単語は説明のために使っているものでSwiftで一般的に用いられる用語ではありません)
コンパイラはコンパイル時に各変数/定数などに型というラベルを張り付けます。
コンパイラはそのラベルを頼りに使用可能なメソッドやプロパティを決定します。
swift
1let a = 0 2let b = a as Any
としたとき実体としてのbはIntのままですが、コンパイラが認識するラベルとしての型はAnyになります。
ですのでbに対してIntのメソッドやプロパティを使用しようとするとコンパイルエラーとなります。
しかし、type(of:)はラベルではなく実体を見ていますので実体のMetatypeを返します。
投稿2019/12/02 14:15
編集2019/12/03 02:05総合スコア3391
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/12/02 23:22
2019/12/03 01:53
2019/12/03 02:05
2019/12/03 02:07
2019/12/03 02:20 編集
2019/12/03 02:27 編集
2019/12/03 02:33
2019/12/03 02:33
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/12/02 12:49
2019/12/02 13:03
2019/12/02 23:20 編集
2019/12/03 01:03 編集
2019/12/03 02:04 編集
2019/12/03 04:09 編集
2019/12/03 04:18 編集