2015-01-13 20 views
27

ジェネリックを使用して次の構造を実装しようとしています。奇妙なコンパイラエラーを取得して、理由を把握することはできません。奇妙なジェネリックスエラー

class Translator<T:Hashable> {...} 

class FooTranslator<String>:Translator<String> {...} 

Translatorは、Tを辞書のキーのタイプとして使用します。これは、例えば、 Stringまたはenumです。サブクラスは具体的な辞書を提供します。

しかし、それはこのために失敗した:「型 『string』は、プロトコルに準拠していない 『ハッシュ可能』」

が、文字列は、ハッシュ可能に準拠!私はナッツですか?また、Hashableにも準拠しているIntでは動作しません。また、HashableをEquatableに置き換えても、両方で実装されるはずです。 (私はそこにキーとしてハッシュ可能ではないものを使用することはできませんよう、私も辞書を無効にする必要が)私はテストのために、型制約を削除した場合

- それはコンパイル

class Translator<T> {...} 

class FooTranslator<String>:Translator<String> {...} 

私は何をしています間違っている?

答えて

17

私はスウィフト、開発者ではないんだけど、Javaで同様の問題を見た、私はあなたがclass FooTranslator<String>を宣言しているため、問題は、現時点であなたがStringと呼ばれるタイプのパラメータを宣言していることである疑いがある - そうタイプ引数Translator<String>は、型パラメータであり、制約はありません。あなたはは、私は疑う、全くタイプのパラメータしたくない(つまり、あなたのFooTranslatorはジェネリッククラス自体になりたくない。)

スウィフトsubclasses of a generic class also have to be genericで、コメントで指摘したように。あなたは、おそらくこのように、使い捨て型パラメータを宣言することができます:

class FooTranslator<T>:Translator<String> 

まだ問題を引き起こしていたものだったStringと呼ばれる新しいタイプのパラメータを、宣言回避します。これは、例えば、あなたが本当にサブクラスを必要とする前提ですべてです...あなたは任意の型パラメータを望んでいないが、それは何よりも、おそらく良いでしょうときに、新しいタイプのパラメータを導入している

を意味し、メンバーを追加または無効にする。でも

typealias FooTranslator = Translator<String> 

それとも、本当にしたい場合は、恐ろしい方法で両者を混ぜる:あなただけTranslator<String>として正確同じタイプをしたい場合一方、あなたの代わりにタイプエイリアスを使用する必要がありますサブクラスが、一般的な方法でそれを参照する必要がありますする必要はありません:

class GenericFooTranslator<T>:Translator<String> 
typealias FooTranslator = GenericFooTranslator<Int> 

TranslatorTは同じではないことを示すために、ここでIntが意図的にStringではないことに注意してくださいTFooTranslator。手始めに)

+0

これはそうではありません。これに関する迅速な問題/制限があります。http://stackoverflow.com/questions/24138359/limitation-with-classes-derived-from-generic-classes-in-swift – Ixx

+0

@lxx:Ickを参照してください。私はそれがまだ*原因*だと思っていますが、あなたは別の解決策が必要です。試してみる可能性のある解決法で私の答えを編集しました。 –

+0

さて、はい、捨て型のパラメータを持つソリューションは動作しますが、醜いので試していませんでした。反対側で、私はもう一度typealiasソリューションを試して、今すぐ動作します。 (選択された答え)は、文字列、パラメータを使用しようとしたが、エラーを引き起こしていた理由は、プレースホルダとしてIntを使用するため、問題がありました。私はそれをTと置き換えて、コンパイルするのではなく、クラスタブルを宣言しました。それはまだ解決策です。とにかく私に正しい解決策を教えてくれてありがとうと思っています:) – Ixx

96

、のがエラーを説明しましょう:

を考える:ここ

class Foo<T:Hashable> { } 

class SubFoo<String> : Foo<String> { } 

紛らわしい部分は、私たちが「文字列」の文字のコレクションを保持スウィフト定義された構造を意味することを期待していることです。しかし、そうではありません。

ここで、「String」は、新しいサブクラス「SubFoo」を指定したジェネリック型の名前です。

class SubFoo<String> : Foo<T> { } 

この行が宣言されていないタイプの使用などTためにエラーが発生します。私たちはいくつかの変更を加えた場合、これは非常に明白になります。

はその後、我々はこれに行を変更した場合:

class SubFoo<T> : Foo<T> { } 

私たちが戻ってあなたがもともと持っていた同じエラーにしている、「T」「はハッシュ可能」に準拠していません。ここでは明らかです。なぜなら、Tは 'Hashable'に従う既存のSwiftタイプの名前を混乱させるものではないからです。明らかに 'T'は一般的なものです。

「文字列」と書くと、それは汎用タイプのプレースホルダ名でもあり、実際にはSwiftに存在するStringタイプではありません。


我々は、一般的なクラスの特定のタイプごとに異なる名前を使用する場合は、適切なアプローチはほぼ確実typealiasです:

class Foo<T:Hashable> { 

} 

typealias StringFoo = Foo<String> 

これは完全に有効なスウィフトであり、それはうまくコンパイルします。


実際にサブクラス化し、ジェネリッククラスにメソッドやプロパティを追加することで、我々が望むものを代わりにした場合、我々は必要なものを私たちが必要なものに私たちの一般的な、より具体的になりますクラスまたはプロトコルです。元の問題に戻って

、最初のエラーを取り除くましょう:

class Foo<T: Hashable> 

class SubFoo<T: Hashable> : Foo<T> { } 

これは完全に有効なスウィフトです。しかし、それは特に我々がやっていることには役に立たないかもしれません。私たちは、次の操作を行うことはできません

唯一の理由は:Stringはスウィフトクラスではないので、

class SubFoo<T: String> : Foo<T> { } 

単純である - それは構造です。そして、これはどんな構造にも許されません。


我々はHashableから継承する新しいプロトコルを記述する場合、我々はそれを使用することができます。

protocol MyProtocol : Hashable { } 

class Foo<T: Hashable> { } 
class SubFoo<T: MyProtocol> : Foo<T> { } 

これは完全に有効です。

また、私たちが実際にHashableから継承する必要はありませんのでご注意:

protocol MyProtocol { } 
class Foo<T: Hashable> { } 
class SubFoo<T: Hashable, MyProtocol> { } 

また、これは完全に有効です。

ただし、何らかの理由で、Swiftはここでクラスを使用させません。たとえば、次のように

class MyClass : Hashable { } 

class Foo<T: Hashable> { } 
class SubFoo<T: MyClass> : Foo<T> { } 

スウィフトは不思議な「T」の私たちがそれを作るために必要なコードを追加しても、「ハッシュ可能」(に準拠していないと文句を言い


最後に、右を。最もスウィフトに適したアプローチは、「Hashable」から継承し、必要な機能を追加する新しいプロトコルを作成する予定です。

サブクラスでStringを受け入れることは厳密には重要ではありません。それは、私たちのサブクラスには、実行中の作業に必要なメソッドとプロパティがあります。