繁体   English   中英

为什么泛型不能编译?

[英]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 ,以表明TTranslator是不一样的TFooTranslator 。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM