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/26 04:49
2018/11/26 11:38