2015-01-13 57 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应该很重要我们的子类需要它,它具有我们所需要的任何方法和属性。