2015-01-13 75 views
27

我想實現下面的結構使用泛型。得到一個奇怪的編譯器錯誤,不知道爲什麼。奇怪的泛型錯誤

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

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

這個想法是,翻譯器使用T作爲字典中的鍵的類型。這可以是例如一個字符串或枚舉。子類提供了具體的字典。

但它失敗,因爲這個:「類型‘串’不符合協議‘哈希的’」

但字符串與哈希的!我瘋了嗎?它也不適用於Int,它也符合Hashable。如果我用Equatable替換Hashable,它也不起作用,這也應該由兩者來實現。

如果我刪除了類型約束,只是爲了測試(在那裏我也有禁用字典,因爲我不能使用任何不哈希的關鍵有) - 它編譯

class Translator<T> {...} 

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

我是什麼做錯了?

回答

17

我不是斯威夫特開發者,但是看在Java中類似的問題,我懷疑的問題是,此刻的你正在聲明呼籲String因爲你聲明class FooTranslator<String>類型參數 - 這樣的類型參數 in Translator<String>就是那種類型的參數,它沒有約束。你不希望一個類型參數在所有的,我懷疑(即你不希望你的FooTranslator是一個泛型類本身)。

正如在評論中指出,在斯威夫特subclasses of a generic class also have to be generic。你可能宣佈扔掉的類型參數,就像這樣:

class FooTranslator<T>:Translator<String> 

仍然避免聲明名爲String一個新的類型參數,這是什麼原因導致的問題。這意味着你要介紹一個新的類型參數的時候你不在希望任何類型的參數,但它可能比沒有好...

這一切,前提是你真的需要一個子類,例如添加或覆蓋成員。在另一方面,如果你只是想要一個類型,是正是一樣Translator<String>,你應該使用一個類型別名來代替:

typealias FooTranslator = Translator<String> 

甚至一個可怕的方式將兩者攪和,如果你真的想一個子類,但不希望有指它在一個通用的方法:

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

(注意,這裏的Int是故意不String,表明在TranslatorT是不一樣的T in FooTranslator。)

+0

不是這樣。有關於此的Swift問題/限制,請參閱http://stackoverflow.com/questions/24138359/limitation-with-classes-derived-from-generic-classes-in-swift – Ixx

+0

@lxx:Ick。我懷疑這仍然是*原因*,但你想要一個不同的解決方案。我用可能的解決方案編輯了我的答案,以供嘗試。 –

+0

嗯,是的,拋棄類型參數的解決方案有效,但我沒有嘗試它,因爲它很醜。另一方面,我再次嘗試使用typealias解決方案,它現在可以工作。有一個問題,因爲(選擇答案)它使用Int作爲佔位符,這就是爲什麼我嘗試使用String,參數並導致錯誤。我用T取代了它,並且聲明瞭類中的typealias,而不是它編譯的。但它仍然是一種解決方法。謝謝反正我指回到正確的解決方案:) – Ixx

96

首先,讓我們解釋的錯誤:

考慮:

class Foo<T:Hashable> { } 

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

這裏的混亂的部分是,我們希望「字符串」是指雨燕定義的結構保持字符的集合。但事實並非如此。

在這裏,「字符串」是我們給出新子類SubFoo的泛型類型的名稱。

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

此行T產生一個錯誤的使用了未經申報類型的:如果我們做出一些改變,這變得非常明顯。

那麼如果我們改變行這樣:

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

我們回到你原來有同樣的錯誤,「T」不符合「哈希的」。在這裏很明顯,因爲T並沒有混淆現有Swift類型的名稱,它恰好符合'Hashable'。很明顯'T'是一個通用的。

當我們寫'String'時,它也只是泛型類型的佔位符名稱,實際上並不是Swift中存在的String類型。


如果我們要對特定類型的泛型類的不同的名稱,相應的處理方法幾乎可以肯定是一個typealias

class Foo<T:Hashable> { 

} 

typealias StringFoo = Foo<String> 

這是完全有效的斯威夫特,並將其編譯就好了。


相反,如果我們要的是真正繼承並添加方法或屬性的泛型類,那麼我們需要的是一個類或協議,這將使我們的通用更具體的我們所需要的。

讓我們回到最初的問題,讓我們首先擺脫錯誤的:

class Foo<T: Hashable> 

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

這是完全有效的斯威夫特。但它對我們正在做的事情可能並不特別有用。

,我們不能做以下的唯一原因:

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

很簡單,因爲String不是斯威夫特類 - 這是一個結構。這對於任何結構都是不允許的。


如果我們寫了一個新的協議,從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」不符合「哈希的」(甚至當我們添加必要的代碼來使其


最後,正確的。方法,最迅捷,合適的方法將被寫入新的協議,從「哈希的」繼承並添加你需要的任何功能它。

它不應該是嚴格重要的是,我們的子類接受String。 wha應該很重要我們的子類需要它,它具有我們所需要的任何方法和屬性。