2017-08-10 20 views
2

に私は、フォームの一般的なクラスを持っている:継承、ジェネリック、およびプロトコルは、スウィフト

class BaseClass<T> { 
    var prop: T 
    ... 
} 

私は、フォームの複数のサブクラスを持っている:

class SubClassOne: BaseClass<SomeSubClass> { 
    ... 
} 

class SubClassTwo: BaseClass<SomeOtherSubClass> { 
    ... 
} 

型パラメータSomeSubClassSomeOtherSubClassどちらも共通の基本クラスSomeBaseClassを継承しています。

ここで、SubClassOneSubClassTwoの両方のインスタンスを格納する変数を定義します。値を代入しようとしたとき

var obj: BaseClass 
var obj: BaseClass<SomeBaseClass> 
var obj: BaseClass<Any> 

しかし、エラーCannot assign value of type 'SubClassOne' to type ...でエラーReference to generic type 'BaseClass' requires arguments in <...>、および他の2つの結果の最初の試みの結果:私は多くの可能性を試してみました。

var testArray = [SubClassOne(), SubClassTwo()] 

しかし、これでもエラー Heterogeneous collection literal could only be inferred to [Any]; add explicit type annotation if this is intentionalで、その結果、失敗しました:私も、配列を初期化することにより、私のためにタイプを推測するにスウィフトコンパイラをだましてみました。実際に、両方のサブクラスの格納に成功する唯一の型注釈は Anyまたは AnyObjectです。より具体的なタイプでこれらのインスタンスを格納することは可能ですか?そうでない場合、なぜですか?

これが重要な理由は、最終的には、格納された変数objからプロパティpropを取得したいということです。 objAnyとして保存されている場合は、私はそうすることができません。また、プロパティにアクセスしようとしているメソッド自体が汎用メソッドであり、キャストするSubClassOneまたはSubClassTwoのどちらがメソッドのジェネリック型パラメータに依存するので、単にSubClassOneまたはSubClassTwoにキャストできません。

func castObj<T>(asType: T.Type) { 
    (self.obj as? T).prop 
} 

castObj(asType: SubClassOne.self)またはcastObj(asType: SubClassTwo.self)と呼ばれるものです。しかし、私たちは同じ問題に遭遇します:SubClassOneSubClassTwoの両方を受け入れる定義できる唯一のジェネリック型パラメータの制約はAnyであり、SwiftコンパイラはValue of type 'T' has no member 'prop'という文句を言います。 Iは所望の特性をカプセル化プロトコルを定義しようとした回避策として

protocol HasProp { 
    var prop: SomeBaseClass { get } 
} 

そしてIはSubClassOneSubClassTwoの宣言にこれを追加しました。しかし、これはさらに別のエラーが発生しました:Type 'SubClassOne' does not conform to protocol 'HasProp'SubClassOneSubClassTwoの両方がpropからBaseClass<SomeSubClass>に継承されているので、実際にはプロトコルに準拠しているので、私も混乱します。要約すると

  1. BaseClassのプロパティへのアクセスを提供し、より具体的なタイプでSubClassOneSubClassTwoのインスタンスを格納することが可能ですか?そうでない場合、なぜですか?
  2. なぜサブクラスはプロトコルに準拠していないのですか?
  3. 希望の動作を達成するためにデザインを変更するにはどうすればよいですか?

答えて

0

現時点では、関数castObjには、汎用パラメータの型制約がありません。Tです。 BaseClassの型制約を与えることで、BaseClassに両方のプロパティがあるので、うまくいくはずです。あなたの例では

func castObj<T: BaseClass>(asType: T.Type) { 
    (self.obj as? T).propOne 
    (self.obj as? T).propTwo 
} 
+0

私もこれを試してみました。最初に 'obj'の型を定義しようとしたときと同じ問題が発生しました。 'BaseClass'の型制約はうまくいきません。' genericClass 'には' 'の引数が必要です。例えば、 'BaseClass 'のようにしようとすると、私は '' SubClassOne.Type '型の値を' BaseClass .Type''型に変換することができません。 'castObj(asType:SubClassOne.self)' – asaini007

+0

この関数は 'BaseClass'の内部かその外側に定義されていますか? –

+0

それは外に定義されています – asaini007

0

propTwoのタイプは、両方のサブクラスに共通だったとpropOneの種類を専門にされました。あなたのデザインを反映させる。

[た]

class BaseClass<T,U> { 
    var propOne: T 
    var propTwo: U 
    ... 
} 
class SubClassOne: BaseClass<SomeSubClass, SomeClass> {} 
class SubClassTwo: BaseClass<SomeOtherSubClass, SomeClass> {} 

ポイントは、基本クラスに共通のものを維持し、あなたの専門分野を構成することである

class BaseClass<U> { 
    var propTwo: U 
    ... 
} 
class SubClassOne<T>: BaseClass<SomeClass> { 
    var propOne: T 
    ... 
} 
class SubClassTwo<T>: BaseClass<SomeClass> { 
    var propOne: T 
    ... 
} 

[可能性]。

+0

'SubClassOne'または' SubClassTwo'ジェネリックそのものは、 'SomeSubClass'と' SomeOtherSubClass'に対してそれぞれ定義されているので、それらの2つのクラスのメソッドとプロパティを使って作成する必要はありません。それらは異なる型パラメータで作成されることを意図していません。私はこのアプローチが 'propTwo'へのアクセスを与えることに同意します。なぜなら、両方のサブクラスは' BaseClass 'の型になるでしょうが、' propOne'へのアクセスを助けるとは思えません。 – asaini007

+0

また、この問題は、2番目の型パラメータ( 'U' /' SomeClass')を完全に削除することで単純化できたと考えています。私はそれが無関係で、問題とは関係ないかもしれないと思うが、問題を混乱させるだけだ。 – asaini007

+0

これを反映する質問が更新されました – asaini007

0

SubclassOneとSubclassTwoが同じ継承階層にあるという根本的な誤解があります。ジェネリック型のため、異なる基本クラスから継承します。それらを混ぜ合わせて一致させることはできません。

考えてみてください。継承を使用すると、あなたのテストの例ではので、あなたは、基本クラスを持ってどこにでも任意のサブクラスを使用することができるはずです。

var testArray = [SubClassOne(), SubClassTwo()] 

は、以下の式の右辺はあることをどのようなタイプを持っているでしょうか?

testArray [0] .prop =何か

そして、この1

testArray [1] .prop =何か。 SubClassOne

propのタイプはSomeSubClassSubClassTwopropのタイプは、SomeOtherSubClassなければなりません。

propSomeBaseClassと宣言されており、BaseClassが汎用である必要がなくなりました。

編集

ないのはなぜプロトコルの仕事?

このプロトコルの問題は、プロパティを基本クラスの型として定義することですが、読み書きが可能です。プロトコルの実装におけるプロパティは、サブクラスのうちの1つに特化したプロパティを持つコントラクトを満たすことができません。なぜなら、他のビットのコードは、ベースクラスのインスタンスをそのプロパティに割り当てる必要があるからです。

protocol MyProtocol 
{ 
    var prop: BaseClass 
} 

struct MyImplementation: MyProtocol 
{ 
    var prop: SubClass 
} 

class BaseClass {} 
class SubClass: BaseClass {} 
class DifferentSubClass: BaseClass {} 

var instance: MyProtocol = MyImplementation() 

instance.prop = DifferentSubClass() 
// Should be legal because the protocol says so but the type of prop in instance is SubClass. 
+0

プロトコルが機能しない理由は何ですか? – asaini007

+0

@ asaini007はい。私は答えを編集します – JeremyP

+0

ありがとう、これは確かにgettableとsettable両方のプロパティを持つプロトコルの問題です。しかし、不思議なことに、Swiftは、同じ名前のプロパティとプロトコルのプロパティの型のサブクラスである型(それが合理的な場合)によって、読み取り専用のプロパティを持つプロトコルを準拠させることさえできません。私は本当になぜこれが許されないのかを知りません。例:http://swift.sandbox.bluemix.net/#/repl/5991c36977dbb8209e7b17c1 – asaini007

関連する問題