泛型类型集合 [英] Generic Types Collection
问题描述
以先前解决的问题为基础,但这又导致了另一个问题.如果协议/类类型存储在集合中,则取回并实例化它们会引发错误.下面是一个假设的例子.该范例基于程序接口而不是实现".
Building on previous question which got resolved, but it led to another problem. If protocol/class types are stored in a collection, retrieving and instantiating them back throws an error. a hypothetical example is below. The paradigm is based on "Program to Interface not an implementation" What does it mean to "program to an interface"?
public protocol ISpeakable {
init()
func speak()
}
class Cat : ISpeakable {
required init() {}
func speak() {
println("Meow");
}
}
class Dog : ISpeakable {
required init() {}
func speak() {
println("Woof");
}
}
//Test class is not aware of the specific implementations of ISpeakable at compile time
class Test {
func instantiateAndCallSpeak<T: ISpeakable>(Animal:T.Type) {
let animal = Animal()
animal.speak()
}
}
// Users of the Test class are aware of the specific implementations at compile/runtime
//works
let t = Test()
t.instantiateAndCallSpeak(Cat.self)
t.instantiateAndCallSpeak(Dog.self)
//doesn't work if types are retrieved from a collection
//Uncomment to show Error - IAnimal.Type is not convertible to T.Type
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for animal in animals {
//t.instantiateAndCallSpeak(animal) //throws error
}
for (index:Int, value:ISpeakable.Type) in enumerate(animals) {
//t.instantiateAndCallSpeak(value) //throws error
}
编辑-我当前的解决方法是遍历集合,但是由于api必须知道各种实现方式,因此这当然是有局限性的.另一个限制是这些类型的子类(例如PersianCat,GermanShepherd)将不会调用其重写的函数,否则我将进入Objective-C进行救援(NSClassFromString等),或者等待SWIFT支持此功能.
Edit - My current workaround to iterate through collection but of course it's limiting as the api has to know all sorts of implementations. The other limitation is subclasses of these types (for instance PersianCat, GermanShepherd) will not have their overridden functions called or I go to Objective-C for rescue (NSClassFromString etc.) or wait for SWIFT to support this feature.
注意(背景):实用程序的用户将这些类型推送到数组中,并在通知时执行for循环
Note (background): these types are pushed into array by users of the utility and for loop is executed on notification
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for Animal in animals {
if Animal is Cat.Type {
if let AnimalClass = Animal as? Cat.Type {
var instance = AnimalClass()
instance.speak()
}
} else if Animal is Dog.Type {
if let AnimalClass = Animal as? Dog.Type {
var instance = AnimalClass()
instance.speak()
}
}
}
推荐答案
基本上答案是:正确,您不能这样做. Swift需要在编译时而不是在运行时确定类型参数的具体类型.这在很多小的情况下都会出现.例如,您不能构造通用闭包并将其存储在没有类型指定的变量中.
Basically the answer is: correct, you can't do that. Swift needs to determine the concrete types of type parameters at compile time, not at runtime. This comes up in a lot of little corner cases. For instance, you can't construct a generic closure and store it in a variable without type-specifying it.
如果我们将其简化为最小的测试用例,则可能会更清楚
This can be a little clearer if we boil it down to a minimal test case
protocol Creatable { init() }
struct Object : Creatable { init() {} }
func instantiate<T: Creatable>(Thing: T.Type) -> T {
return Thing()
}
// works. object is of type "Object"
let object = instantiate(Object.self) // (1)
// 'Creatable.Type' is not convertible to 'T.Type'
let type: Creatable.Type = Object.self
let thing = instantiate(type) // (2)
在第1行,编译器有一个问题:在instantiate
的实例中,T
应该是什么类型?这很容易,应该为Object
.这是一个具体的类型,所以一切都很好.
At line 1, the compiler has a question: what type should T
be in this instance of instantiate
? And that's easy, it should be Object
. That's a concrete type, so everything is fine.
在第2行,没有任何具体类型可以让Swift生成T
.它所具有的只是Creatable
,它是一种抽象类型(我们通过代码检查知道type
的实际值,但是Swift并不考虑该值,而只是类型).可以接受和返回协议,但是不能将它们设置为类型参数.今天只是不合法的Swift.
At line 2, there's no concrete type that Swift can make T
. All it has is Creatable
, which is an abstract type (we know by code inspection the actual value of type
, but Swift doesn't consider the value, just the type). It's ok to take and return protocols, but it's not ok to make them into type parameters. It's just not legal Swift today.