实现泛型的自定义类的数组,不允许具有子类通用的自定义类

时间:2016-03-04 02:33:22

标签: ios swift

我有以下情况

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>())

有人会理解为什么一种方式有效而不是另一种方式吗?

1 个答案:

答案 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>