泛型类型集合

用户名

在解决之前的问题的基础上,但又导致了另一个问题。如果协议/类类型存储在集合中,则取回并实例化它们会引发错误。下面是一个假设的例子。该范例基于“程序到接口而不是实现”,“程序到接口”是什么意思?

从protocol.Type实例在运行时动态实例化

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支持此功能。

注意(背景):实用程序的用户将这些类型推送到数组中,并在通知时执行for循环

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需要在编译时而不是在运行时确定类型参数的具体类型。这在很多小的情况下都会出现。例如,您不能构造通用闭包并将其存储在没有类型指定的变量中。

如果我们将其简化为最小的测试用例,则可能会更清晰一些

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行,编译器有一个问题:T在这种情况下,哪种类型instantiate这很容易,应该是Object这是一个具体的类型,所以一切都很好。

在第2行,Swift不能创建任何具体类型T它所具有的只是Creatable一个抽象类型(通过代码检查我们知道的实际值type,但是Swift并不考虑该值,而只是类型)。可以接受和返回协议,但是不能将它们变成类型参数。今天只是不合法的Swift。

Swift编程语言:通用参数和参数中暗示了这一点

声明泛型类型,函数或初始化程序时,请指定泛型类型,函数或初始化程序可以使用的类型参数。这些类型参数充当占位符,当创建泛型类型的实例或调用泛型函数或初始化程序时,这些类型参数将由实际的具体类型参数替换(强调我的)

在Swift中,您将需要做的另一种尝试。

作为一项有趣的奖励,尝试明确要求不可能的事情:

let thing = instantiate(Creatable.self)

而且...迅速崩溃。


从您的进一步评论中,我认为闭包确实可以满足您的需求。您已使协议要求构造简单(init()),但这是不必要的限制。您只需要调用者告诉函数如何构造对象。使用闭包很容易,并且完全不需要这种类型的参数化。这不是解决方法;我相信这是实现您描述的模式的更好方法。考虑以下内容(一些小的更改,以使示例更像Swift):

// Removed init(). There's no need for it to be trivially creatable.
// Cocoa protocols that indicate a method generally end in "ing" 
// (NSCopying, NSCoding, NSLocking). They do not include "I"
public protocol Speaking {
    func speak()
}

// Converted these to structs since that's all that's required for
// this example, but it works as well for classes.
struct Cat : Speaking {
    func speak() {
        println("Meow");
    }
}

struct Dog : Speaking {
    func speak() {
        println("Woof");
    }
}

// Demonstrating a more complex object that is easy with closures,
// but hard with your original protocol
struct Person: Speaking {
    let name: String
    func speak() {
        println("My name is \(name)")
    }
}

// Removed Test class. There was no need for it in the example,
// but it works fine if you add it.
// You pass a closure that returns a Speaking. We don't care *how* it does
// that. It doesn't have to be by construction. It could return an existing one.
func instantiateAndCallSpeak(builder: () -> Speaking) {
    let animal = builder()
    animal.speak()
}

// Can call with an immediate form.
// Note that Cat and Dog are not created here. They are not created until builder()
// is called above. @autoclosure would avoid the braces, but I typically avoid it.
instantiateAndCallSpeak { Cat() }
instantiateAndCallSpeak { Dog() }

// Can put them in an array, though we do have to specify the type here. You could
// create a "typealias SpeakingBuilder = () -> Speaking" if that came up a lot.
// Again note that no Speaking objects are created here. These are closures that
// will generate objects when applied.
// Notice how easy it is to pass parameters here? These don't all have to have the
// same initializers.
let animalBuilders: [() -> Speaking] = [{ Cat() } , { Dog() }, { Person(name: "Rob") }]

for animal in animalBuilders {
    instantiateAndCallSpeak(animal)
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

特定枚举类型的“集合”,但具有泛型

来自分类Dev

泛型-集合中的Java集合

来自分类Dev

泛型:为什么实现的集合返回一个对象而不是指定的类型?

来自分类Dev

在Java中将子类添加到通配符类型的泛型集合

来自分类Dev

C#集合类型约束泛型

来自分类Dev

泛型类型的转换

来自分类Dev

返回C#中作为集合的泛型类型

来自分类Dev

泛型类型子类

来自分类Dev

泛型类型,集合和对象引用

来自分类Dev

具有泛型类型的泛型集合生成

来自分类Dev

集合对泛型类型进行排序Java

来自分类Dev

Spring RestTemplate和泛型类型ParameterizedTypeReference集合,例如List <T>

来自分类Dev

返回泛型类型,它是基于传递给参数的类型的特定集合

来自分类Dev

泛型类型的功能

来自分类Dev

如何为泛型类指定泛型集合类型?

来自分类Dev

返回泛型类型的泛型方法

来自分类Dev

Python 3.9和PEP 585中的任何内容-标准集合中的类型提示泛型

来自分类Dev

检查泛型的类型

来自分类Dev

IntelliJ IDEA自动完成使用泛型的集合类型声明

来自分类Dev

从泛型推断类型

来自分类Dev

Java泛型:某种类型的super的super的集合

来自分类Dev

具有泛型类型的泛型集合生成

来自分类Dev

返回泛型类型,它是基于传递给参数的类型的特定集合

来自分类Dev

泛型类型的功能

来自分类Dev

@JmsListeners泛型类型?

来自分类Dev

获取泛型类型

来自分类Dev

转换泛型类型

来自分类Dev

投射泛型集合项类型

来自分类Dev

泛型类型推断的 Typescript 泛型