在解决了之前的问题的基础上,但又导致了另一个问题。如果协议/类类型存储在集合中,则取回并实例化它们会引发错误。下面是一个假设的例子。该范例基于“程序到接口而不是实现”,“程序到接口”
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)
}
问题内容: 如果我有一个通用类: 我想实例化几个项目,例如… …并将它们添加到集合中。如何定义集合,使其可以容纳泛型类型列表?然后,我想在某个时刻迭代集合,并使用Value属性。可能? 问题答案: 让您的泛型类从非泛型基类继承,或实现一个非泛型接口。然后,您可以拥有此类型的集合,并将其转换为用于访问集合内容的任何代码。 这是一个例子。
我想让一个函数返回一个保证实现两个接口的对象。编译时不一定知道确切的对象。我的代码看起来像: 在尝试编译时,我遇到以下错误: 你好世界java:13:错误:不兼容的类型:C无法转换为T 返回新的C() ^ 其中T是一个类型变量: T扩展了方法f(布尔值) HelloWorld中声明的a,B。java:14:错误:不兼容的类型:D无法转换为T 返回新的D() ^ 其中T是一个类型变量: T扩展了方法
泛型类型 除了反省函数, Swift允许你定义自己的泛型类型. 它们是可以用于任意类型的自定义类、结构体、枚举, 和Array、Dictionary方式类型. 1. 定义泛型类型 定义一个普通的结构体 struct IntStack { var items = [Int]() mutating func push(_ item: Int) { items.appen
如何获取这个类的类型?对于上下文,我使用ModelMapper,我需要类类型T从S转换为T。 背景: 我已经尝试了N种方法,其中我放置了“//一些方法来获取类型”,但没有任何效果。例如: 或
我试图在一个方法中使用泛型,在这个方法中,我将一个json反序列化为pojo,这样它就可以返回任何对象类型。 这里我的代码: 问题是
类型和泛型 类型系统的首要目的是检测程序错误。类型系统有效的提供了一个静态检测的有限形式,允许我们代码中明确某种类型的变量并且编译器可以验证。类型系统当然也提供了其他好处,但错误检测是他存在的理由(Raison d’Être) 我们使用类型系统应当反映这一目标,但我们必须考虑到读者(译注:读你代码的人):明智地使用类型可以增加清晰度,而过份聪明只会迷乱。 Scala的强大类型系统是学术探索和实践共