我有以下情况
class Human {}
class Child: Human {}
class Person<T: Human> {}
var people = [Person<Human>]()
people.append(Person<Child>())
但是在people.append(Person<Child>())
行上我收到错误
cannot convert value of type 'Person<Child>' to expected argument type 'Person<Human>'
这很奇怪,因为做了以下工作(这似乎是一个相同的情况)
var myArray = [Array<UIView>]()
myArray.append(Array<UIImageView>())
有人会理解为什么一种方式有效而不是另一种方式吗?
答案 0 :(得分:5)
实际上你并没有足够强烈地提起诉讼。定义:
class Human {}
class Child: Human {}
struct Holder<T> {}
我使Holder成为一个结构,所以没有人可以指责我们作弊:Array是一个结构,Holder是一个结构。我把占用者的约束权拿走了,把所有东西都缩减到最简单的形式。
现在只需指定一个Child数组,其中包含一个Human数组:
var arr = Array<Human>()
arr = Array<Child>()
精细。现在尝试使用持有人:
var holder = Holder<Human>()
holder = Holder<Child>() // error
并行性现在看来很完美:Array是一个结构,Holder是一个结构,我们所做的只是尝试多态分配。那有什么问题?
正如您可能已经怀疑的那样,问题在于您不是Apple。 Apple编写代码,因此他们可以在参数化类型上定义Array和类似的类型 covariant 。但它不是该语言的自动功能 - 也就是说,对于泛型一般并非如此。特别是,你不能为你定义的类型做到这一点。
所以Apple的Array是协变的,但是你的Holder(或Person)不是,并且没有任何东西允许你切换协方差。
您可以看到为什么数组是协变的。这是一个非常特殊的案例。数组是对象的集合。 Apple知道,例如,Child对象的数组实际上也是Human对象的数组,因为每个Child都是Human(多态)。因此,他们已经为数组实现了协方差,以确保这一点。
但是您的人或我的持有人没有这样的保证。 Swift不知道你打算用占位符T做什么你可能会想到用Holder<Child>
取代Holder<Human>
预期的的情况错。所以Apple没有在这方面做出任何假设。
我应该补充一点,区分以下内容非常重要:
class Human {}
class Child: Human {}
struct Holder<T> {
let thing : T
}
let holder : Holder<Human> = Holder(thing:Child()) // fine
这是合法的,但它与我们一直在谈论的内容毫无关系。此处只涉及一种通用类型:Holder<Human>
。我们所做的只是将一个孩子分配到thing
,其中人类是预期的。这是一种很好的老式非泛型多态性。但是,即使Holder<Human>
是一个孩子,你仍然无法将Holder<Child>
转换为thing
,但你仍然无法指定Holder<Child>
1}}预计会Holder<Human>
。