iOS开发笔记iOS 日记本App优化

Swift数组不具备协变特性

2021-04-22  本文已影响0人  long弟弟

标题就是结论,牢记!

Swift数组不具备协变特性,OC数组也不具备协变特性

无效攻击.JPG

《Swift进阶》一书中提到Swift的数组不具备协变特性。

译者也给出了注解,但要熟悉Java或.NET才能理解。iOSer表示概念都跟不上...

译者注:数组的协变特性指的是,包含有子类型对象的数组,可以直接赋值给包含有父类型对象的数组的变量。比如在Java和C#中string是object的子类型,而对应的数组类型string[ ]可以直接赋值给声明为object[ ]类型的变量。但是在Swift中,Array<Parent>和Array<Child>之间并没有这样的关系。

看到注解是不是很懵逼?

Im OK.png

这里就术语结合代码谈论一下自己的理解,希望可以达到抛砖引玉的效果!

术语

__covariant(协变):子类转父类
__contravariant(逆变):父类转子类

协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。

参考:泛型中的协变和逆变

理解:协变就是用一个子类对象去替换相应的一个父类对象,这是完全符合里氏替换原则的。(里氏替换原则:任何基类可以出现的地方,子类一定可以出现。)
逆变就是反过来用一个父类对象去替换相应的一个子类对象。

理解了概念我们再从代码的角度来看一下!

代码

//Swift
class Animal {
    
}
class Dog: Animal {
    
}
class Cat: Animal {
    
}
var arr: [Animal] = [Cat]()
//Swift不认为[Cat]是[Animal]的子类型,但在其中添加了一个隐式转换,现在arr就是[Animal]类型
arr.append(Animal())
//arr的元素是Animal类型的,Animal就是它的类型
arr.append(Dog())
//arr的元素是Animal类型的,Dog是它的子类型
arr.append(Cat())
//arr的元素是Animal类型的,Cat是它的子类型

print(arr)
//[SwiftCovariant.Animal, SwiftCovariant.Dog, SwiftCovariant.Cat]

上述Swift代码能正确执行,所以是隐式转换而不具有协变特性

//Java
Animal[] arr = new Cat[2]; //Java认为Cat[]是Animal[]的子类型
arr[0] = new Cat(); //arr的元素是Cat类型的,存储成功
arr[1] = new Dog(); //arr是Cat类型的,不能接受Dog类型的元素

Java代码运行报错了,因为数组类型Cat[ ]可以直接赋值给声明为Animal[ ]类型的变量,数组的协变性导致了上述错误。

参考:Swift 为什么会犯数组协变这么明显的设计错误?

回顾

了解了概念,结合着代码再回顾术语,再看书中的内容,是不是恍然大悟了😏

上一篇下一篇

猜你喜欢

热点阅读