为什么泛型不能编译?
Why generics don't compile?
我正在尝试使用泛型实现以下结构。遇到编译器错误,无法弄清楚原因。
class Translator<T:Hashable> {...}
class FooTranslator<String>:Translator<String> {...}
想法是翻译器使用 T 作为字典中键的类型。这可以是例如一个字符串或一个枚举。子类提供了具体的字典。
但它失败了,因为:"Type 'String' does not conform to protocol 'Hashable'"
但是String符合Hashable。它也不适用于同样符合 Hashable 的 Int。
如果我删除类型约束,只是为了测试(我还必须禁用字典,因为我不能在那里使用任何不可哈希的东西作为键)- 它编译
class Translator<T> {...}
class FooTranslator<String>:Translator<String> {...}
我做错了什么?
我不是 Swift 开发人员,但在 Java 中看到了类似的问题,我怀疑问题是目前你正在声明一个名为 [=13= 的类型参数] 因为你要声明 class FooTranslator<String>
- 所以 Translator<String>
中的类型 argument 只是那个类型参数,没有约束。我怀疑你 根本不需要 类型参数(即你不希望你的 FooTranslator
本身是一个通用的 class。)
如评论中所述,在 Swift subclasses of a generic class also have to be generic 中。您可以声明一个一次性类型参数,如下所示:
class FooTranslator<T>:Translator<String>
这仍然避免声明一个名为 String
的新类型参数,这就是导致问题的原因。这意味着当您不 想要 任何类型参数时,您正在引入一个新的类型参数,但这可能总比没有好...
这一切都基于您确实需要一个潜艇的假设class,例如添加或覆盖成员。另一方面,如果您只想要 完全 与 Translator<String>
相同的类型,则应改用类型别名:
typealias FooTranslator = Translator<String>
如果你真的想要一个子class但又不想以通用的方式引用它,甚至可以以一种可怕的方式混合两者:
class GenericFooTranslator<T>:Translator<String>
typealias FooTranslator = GenericFooTranslator<Int>
(注意这里的Int
故意不是String
,表明Translator
中的T
和[=21=不一样] 在 FooTranslator
.)
首先,让我们解释一下错误:
给定:
class Foo<T:Hashable> { }
class SubFoo<String> : Foo<String> { }
这里令人困惑的部分是我们期望 "String" 表示包含字符集合的 Swift 定义的结构。但事实并非如此。
这里,"String" 是我们为新子class、SubFoo
赋予的泛型类型的名称。如果我们进行一些更改,这将变得非常明显:
class SubFoo<String> : Foo<T> { }
此行生成 T
错误,因为使用了未声明的类型。
然后如果我们将此行更改为:
class SubFoo<T> : Foo<T> { }
我们又回到了您最初遇到的相同错误,'T' 不符合 'Hashable'。在这里很明显,因为 T 不是恰好符合 'Hashable' 的现有 Swift 类型的名称。很明显 'T' 是泛型。
当我们写'String'时,它也只是泛型类型的占位符名称,而不是Swift中实际存在的String
类型。
如果我们想为泛型的特定类型取一个不同的名称 class,合适的方法几乎肯定是 typealias
:
class Foo<T:Hashable> {
}
typealias StringFoo = Foo<String>
这是完全有效的 Swift,并且编译得很好。
如果我们想要的实际上是子 class 并向泛型 class 添加方法或属性,那么我们需要的是一个 class 或协议,使我们的更具体地满足我们的需求。
回到最初的问题,我们先排除错误:
class Foo<T: Hashable>
class SubFoo<T: Hashable> : Foo<T> { }
这是完全正确的 Swift。但是对于我们正在做的事情可能不是特别有用。
我们无法执行以下操作的唯一原因:
class SubFoo<T: String> : Foo<T> { }
只是因为 String
不是 Swift class——它是一个结构。这对于任何结构都是不允许的。
如果我们编写一个继承自 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。例如:
class MyClass : Hashable { }
class Foo<T: Hashable> { }
class SubFoo<T: MyClass> : Foo<T> { }
Swift 神秘地抱怨 'T' 不符合 'Hashable'(即使我们添加了必要的代码来实现它。
最后,正确的方法,也是最 Swift 合适的方法是编写一个继承自 'Hashable' 的新协议,并向其添加您需要的任何功能。
我们的 subclass 接受 String
并不是严格意义上的重要。重要的是,无论我们的 subclass 采用什么,它都具有我们正在做的任何事情所需的必要方法和属性。
我正在尝试使用泛型实现以下结构。遇到编译器错误,无法弄清楚原因。
class Translator<T:Hashable> {...}
class FooTranslator<String>:Translator<String> {...}
想法是翻译器使用 T 作为字典中键的类型。这可以是例如一个字符串或一个枚举。子类提供了具体的字典。
但它失败了,因为:"Type 'String' does not conform to protocol 'Hashable'"
但是String符合Hashable。它也不适用于同样符合 Hashable 的 Int。
如果我删除类型约束,只是为了测试(我还必须禁用字典,因为我不能在那里使用任何不可哈希的东西作为键)- 它编译
class Translator<T> {...}
class FooTranslator<String>:Translator<String> {...}
我做错了什么?
我不是 Swift 开发人员,但在 Java 中看到了类似的问题,我怀疑问题是目前你正在声明一个名为 [=13= 的类型参数] 因为你要声明 class FooTranslator<String>
- 所以 Translator<String>
中的类型 argument 只是那个类型参数,没有约束。我怀疑你 根本不需要 类型参数(即你不希望你的 FooTranslator
本身是一个通用的 class。)
如评论中所述,在 Swift subclasses of a generic class also have to be generic 中。您可以声明一个一次性类型参数,如下所示:
class FooTranslator<T>:Translator<String>
这仍然避免声明一个名为 String
的新类型参数,这就是导致问题的原因。这意味着当您不 想要 任何类型参数时,您正在引入一个新的类型参数,但这可能总比没有好...
这一切都基于您确实需要一个潜艇的假设class,例如添加或覆盖成员。另一方面,如果您只想要 完全 与 Translator<String>
相同的类型,则应改用类型别名:
typealias FooTranslator = Translator<String>
如果你真的想要一个子class但又不想以通用的方式引用它,甚至可以以一种可怕的方式混合两者:
class GenericFooTranslator<T>:Translator<String>
typealias FooTranslator = GenericFooTranslator<Int>
(注意这里的Int
故意不是String
,表明Translator
中的T
和[=21=不一样] 在 FooTranslator
.)
首先,让我们解释一下错误:
给定:
class Foo<T:Hashable> { }
class SubFoo<String> : Foo<String> { }
这里令人困惑的部分是我们期望 "String" 表示包含字符集合的 Swift 定义的结构。但事实并非如此。
这里,"String" 是我们为新子class、SubFoo
赋予的泛型类型的名称。如果我们进行一些更改,这将变得非常明显:
class SubFoo<String> : Foo<T> { }
此行生成 T
错误,因为使用了未声明的类型。
然后如果我们将此行更改为:
class SubFoo<T> : Foo<T> { }
我们又回到了您最初遇到的相同错误,'T' 不符合 'Hashable'。在这里很明显,因为 T 不是恰好符合 'Hashable' 的现有 Swift 类型的名称。很明显 'T' 是泛型。
当我们写'String'时,它也只是泛型类型的占位符名称,而不是Swift中实际存在的String
类型。
如果我们想为泛型的特定类型取一个不同的名称 class,合适的方法几乎肯定是 typealias
:
class Foo<T:Hashable> {
}
typealias StringFoo = Foo<String>
这是完全有效的 Swift,并且编译得很好。
如果我们想要的实际上是子 class 并向泛型 class 添加方法或属性,那么我们需要的是一个 class 或协议,使我们的更具体地满足我们的需求。
回到最初的问题,我们先排除错误:
class Foo<T: Hashable>
class SubFoo<T: Hashable> : Foo<T> { }
这是完全正确的 Swift。但是对于我们正在做的事情可能不是特别有用。
我们无法执行以下操作的唯一原因:
class SubFoo<T: String> : Foo<T> { }
只是因为 String
不是 Swift class——它是一个结构。这对于任何结构都是不允许的。
如果我们编写一个继承自 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。例如:
class MyClass : Hashable { }
class Foo<T: Hashable> { }
class SubFoo<T: MyClass> : Foo<T> { }
Swift 神秘地抱怨 'T' 不符合 'Hashable'(即使我们添加了必要的代码来实现它。
最后,正确的方法,也是最 Swift 合适的方法是编写一个继承自 'Hashable' 的新协议,并向其添加您需要的任何功能。
我们的 subclass 接受 String
并不是严格意义上的重要。重要的是,无论我们的 subclass 采用什么,它都具有我们正在做的任何事情所需的必要方法和属性。