質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Generics

Genericsはパラメトリックなポリモーフィズムの形態であり、.NET やJavaなど、様々な言語に実装されています。C++のテンプレートと同等の機能を持ち合わせています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

解決済

1回答

5280閲覧

Cannot assign value of type 'A' to type 'T'と出るが理解ができない

solty_919

総合スコア13

Generics

Genericsはパラメトリックなポリモーフィズムの形態であり、.NET やJavaなど、様々な言語に実装されています。C++のテンプレートと同等の機能を持ち合わせています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

0クリップ

投稿2018/11/25 14:28

以下ソースコードがなぜダメなのかが理解できません。

swift

1protocol Sample { 2 var hoge: String { get } 3} 4struct A: Sample { 5 let hoge = "1" 6 let piyo = "1" 7} 8struct B: Sample { 9 let hoge = "2" 10 let piyo = "2" 11} 12struct Person<T: Sample> { 13 let name: String 14 let fuga: T 15 init(name: String) { 16 self.name = name 17 if name.isEmpty { 18 self.fuga = A() //ここでエラー 19 } else { 20 self.fuga = B() //ここでエラー 21 } 22 } 23}

エラーメッセージは以下です

Cannot assign value of type 'A' to type 'T'

T型じゃないから入れられないよって意味だと思うんですがTはSampleプロトコルに準拠していれば
入れれるはずです。AはSampleプロトコルに準拠しているので入るのが僕の考えなのですがどこか間違っているでしょうか?

ちなみにXcodeに任せてコード補完してもらうと

swift

1self.fuga = B() as! T

になります

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Sampleプロトコルに準拠していれば入れれるはずです

質問者さんはTの型の条件を

(A)「Sampleプロトコルと互換性がある型ならなんでもよい」

と捉えておられると思います。それはある面では正しいですが、注意しなければならないのは(A)が

「Personを利用する側が型パラメータTに指定する具象型の条件」

であることです。Personの実装内部においてはTの制約は(A)だけでは不十分でして、そこでは

(B)「Personが用いられている文脈においてTに実際に指定された具象型と互換性がある」

という制約が必要です。次のコードを考えてみてください。

例1:

swift

1let pb: Person<B> = new Person(name: "")

このコードの文脈ではTがBである前提で解釈されます。しかしコンストラクターの中でAのインスタンスをfugaに無条件に代入できたとすると(A)は満たしますが(B)を満たしません。既にお気づきと思いますが「AはBと互換性がない型」だからです。


Personの中でSample型と互換性のある特定の具象型(A, B, etc.)をfugaへ代入したい場合genericsにしないほうがよいと思います。そういうケースではfugaの型は単にSampleでよいのではないでしょうか?

swift

1struct Person { 2 let name: String 3 let fuga: Sample 4 5 init(name: String) { 6 self.name = name 7 if name.isEmpty { 8 self.fuga = A() 9 } else { 10 self.fuga = B() 11 } 12 } 13}

genericsとして定義する意味は例えば

swift

1let pb : Sample<B> = ... 2let b = pb.fuga

このように書いたとき、bが「B型でなければならない」と解釈させたい場合に出てきます。このとき、型引数T型の具体的な値をPersonのメソッド内で得たい場合、普通「メソッドの利用者から引数で指定させる」ことが多いと思います。例えばコンストラクターなら

init(name: String, fuga: T)

のようにしておきます。Tを何型として扱いたいかがわかっているのはPersonを利用する側なわけですからPerson内部では特定の具象型を仮定することなく、利用者から与えてもらう方が分かり易いと思います。


もしどうしてもコンストラクターの中でA, Bなどの具象型の値をfugaに設定したいのであれば、Personの利用者がTを何型と仮定しているかわからない以上、「実際にTに指定された型と互換性があるかどうかをチェック」しなければなりません。そのチェックがすなわちA() as! Tということになります。

なおas!を用いて矛盾があるコード(前述の例1)を実行してみますと、期待通り

Could not cast value of type 'test.A' (0x7f6e6e4b5148) to 'test.B' (0x7f6e6e4b51b8).

という実行時エラーがおきてくれます。
(Ubuntu Swift 4.2でやってみました)

投稿2018/11/25 21:40

編集2018/11/26 04:13
KSwordOfHaste

総合スコア18400

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

solty_919

2018/11/26 04:49

丁寧なご回答ありがとうございました!! 「外からTを指定された時に中のTが必ず一致するとは限らない」 という認識で理解しました。 ご指摘の通り外からfugaの型をSampleにしたいところです。 が希望として`Person.fuga.piyo`のようにアクセスして値を取り出したかったので ジェネリクスを使用していました。現状打開策が見当たらずなのでクラスを使って対処していますが なるべく継承はしたくありません。 structとprotocolで希望が解決される手法がないか再度検討してみます。
KSwordOfHaste

2018/11/26 11:38

元のご質問の範囲を超えてしまいますが... 例に挙げておられるA, BをみるとSampleはhogeだけでなくpiyoというメンバーも持つものと期待しているように見えます。もしそうなら最もシンプルな解決法は protocol Sample {  var hoge: String { get }  var piyo: String { get } } とし、Person.fugaの型をTではなくSampleにすること(Personをgenericsでなくすこと)だと思います。 > なるべく継承はしたくありません この意味がSampleにpiyoを定義したくないという意味ならA, Bの定義例は class A: Person {  let hoge = "1"  let foo = "1" } class B: Person {  let hoge = "2"  let bar = "2" } とし、 let personA = Person<A>("") let fooOfPersonA = personA.fuga.foo let personB = Person<B>("a") let barOfPersonB = personB.fuga.bar のように利用したい と書かないと閲覧者に「何がしたいのか」伝わりにくいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問