[英]Why these generics don't compile in OpenJDK7, but do in OpenJDK6
[英]Why generics don't compile?
我正在尝试使用泛型实现以下结构。 遇到编译器错误,不知道为什么。
class Translator<T:Hashable> {...}
class FooTranslator<String>:Translator<String> {...}
这个想法是 Translator 使用 T 作为字典中键的类型。 这可以是例如字符串或枚举。 子类提供了具体的字典。
但它失败了,因为:“类型 'String' 不符合协议 'Hashable'”
但是String符合Hashable。 它也不适用于 Int,它也符合 Hashable。
如果我删除类型约束,只是为了测试(我还必须禁用字典,因为我不能使用任何不可散列的作为键) - 它编译
class Translator<T> {...}
class FooTranslator<String>:Translator<String> {...}
我究竟做错了什么?
首先,让我们解释一下错误:
鉴于:
class Foo<T:Hashable> { }
class SubFoo<String> : Foo<String> { }
这里令人困惑的部分是我们期望“String”表示包含字符集合的 Swift 定义的结构。 但事实并非如此。
此处,“String”是我们为新子类SubFoo
提供的泛型类型的名称。 如果我们进行一些更改,这将变得非常明显:
class SubFoo<String> : Foo<T> { }
由于使用了未声明的类型,这一行会为T
生成一个错误。
然后,如果我们将行更改为:
class SubFoo<T> : Foo<T> { }
我们又回到了您最初遇到的相同错误,“T”不符合“Hashable”。 在这里很明显,因为 T 不会混淆现有的符合 'Hashable' 的 Swift 类型的名称。 很明显'T'是一个泛型。
当我们编写 'String' 时,它也只是泛型类型的占位符名称,实际上并不是 Swift 中存在的String
类型。
如果我们想为泛型类的特定类型使用不同的名称,那么合适的方法几乎肯定是typealias
:
class Foo<T:Hashable> {
}
typealias StringFoo = Foo<String>
这是完全有效的 Swift,它编译得很好。
相反,如果我们想要的是实际子类化并向泛型类添加方法或属性,那么我们需要的是一个类或协议,使我们的泛型更符合我们的需求。
回到原来的问题,我们先把错误去掉:
class Foo<T: Hashable>
class SubFoo<T: Hashable> : Foo<T> { }
这是完全有效的 Swift。 但它对我们正在做的事情可能不是特别有用。
我们不能做以下事情的唯一原因:
class SubFoo<T: String> : Foo<T> { }
只是因为String
不是 Swift 类——它是一个结构。 这对于任何结构都是不允许的。
如果我们编写一个继承自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> { }
Swift 神秘地抱怨 'T' 不符合 'Hashable'(即使我们添加了必要的代码来使它如此。
最后,正确的方法,也是最适合 Swift 的方法是编写一个新的协议,该协议继承自“Hashable”并添加您需要的任何功能。
我们的子类接受String
不应该是非常重要的。 重要的是,无论我们的子类采用什么,它都具有我们正在做的任何事情所需的必要方法和属性。
我不是 Swift 开发人员,但在 Java 中看到过类似的问题,我怀疑问题是目前您正在声明一个名为String
的类型参数,因为您正在声明class FooTranslator<String>
- 所以类型参数在Translator<String>
就是那种没有约束的类型参数。 你不想在所有类型参数,我怀疑(即你不希望你的FooTranslator
是一个泛型类本身)。
正如评论中所指出的,在 Swift 中,泛型类的子类也必须是 generic 。 您可能会声明一个丢弃类型参数,如下所示:
class FooTranslator<T>:Translator<String>
这仍然避免声明一个名为String
的新类型参数,这是导致问题的原因。 这意味着你介绍时,你不希望任何类型参数的新类型参数,但它可能是聊胜于无...
这一切都假设您确实需要一个子类,例如添加或覆盖成员。 另一方面,如果您只想要一个与Translator<String>
完全相同的类型,则应该使用类型别名:
typealias FooTranslator = Translator<String>
或者甚至以一种可怕的方式将两者混合,如果您真的想要一个子类但不想以通用方式引用它:
class GenericFooTranslator<T>:Translator<String>
typealias FooTranslator = GenericFooTranslator<Int>
(请注意, Int
这里是故意不String
,以表明T
的Translator
是不一样的T
的FooTranslator
。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.