arrays - Swift - 将协议(protocol)数组向上转换为 super 协议(protocol)数组会导致错误

标签 arrays swift protocols upcasting

在 Swift 中,我注意到我可以向上转换一个符合名为 SubProtocol 的协议(protocol)的对象。到另一个名为 SuperProtocol 的协议(protocol)这是SubProtocol的 super 协议(protocol)。但我不能对协议(protocol)数组做同样的事情。这是我在 Playground 中运行的示例代码:

protocol SuperProtocol {
}

protocol SubProtocol: SuperProtocol {
}

class MyObject: SubProtocol {
}

let value1: SubProtocol = MyObject()
let value2: SuperProtocol = value1 // No error here. Upcasting works.

let array1: [SubProtocol] = [MyObject()]
let array2: [SuperProtocol] = array1 // Error here "Cannot convert value of type '[SubProtocol]' to specified type '[SuperProtocol]'"

这似乎违反直觉,我想知道为什么不允许这样做。

最佳答案

原因与协议(protocol)与类的继承方式不同有关。

首先考虑协议(protocol)可以有默认实现,例如:

protocol MammalLocomotion {
    func legs() -> Int
}

extension MammalLocomotion {
    func legs () -> Int {
        return 2
    }
}

protocol CowLocomotion : MammalLocomotion {

}

extension CowLocomotion {
    func legs () -> Int {
        return 4
    }
}

让我们创建符合这些协议(protocol)的类:

class Mammal : MammalLocomotion {

}

class Cow : Mammal, CowLocomotion {

}

let mammal = Mammal()
let cow = Cow()

他们的 legs() 方法的响应符合我们的预期:

mammal.legs() // 2
cow.legs() // 4

但是现在让我们将 cow 转换为 Mammal:

let cowAsMammal : Mammal = cow

cowAsMammal.legs() // 2

cow 有 4 条腿,但现在它有 2。这是因为,对于协议(protocol),当前已知的类型决定了使用哪个默认实现。因此,转换数组不起作用 - 我认为原因是数组转换会意外地改变其包含的对象的行为。

解决方法

正如您所指出的,这行不通:

let farm : [CowLocomotion] = [Cow(), Cow(), Cow()]
let mammalFarm : [MammalLocomotion] = farm // doesn't work

如果需要,您可以通过将数组映射到所需的协议(protocol)来解决此限制:

let farm = [Cow(), Cow(), Cow()]

farm.forEach { print($0.legs()) } // prints 4, 4, 4

let mammalFarm = farm.map { $0 as MammalLocomotion }

mammalFarm.forEach { print($0.legs()) } // prints 2, 2, 2

有关协议(protocol)如何继承的更多信息,请参阅今年 WWDC 的面向协议(protocol)的 Swift 编程 session - transcript here .

关于arrays - Swift - 将协议(protocol)数组向上转换为 super 协议(protocol)数组会导致错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36645974/

相关文章:

ios - 如何在 Swift 中使用扩展协议(protocol)公开 Obj-C 类的现有属性

python - 在python中的二维数组上生成随机线

ios - 如何在应用程序中显示 PDF URL 文件并打开或查看 PDF 文件的下一页,如 iBook

swift 声明一个常量而不让

ios - CloudKit:获取列表属性中具有特定元素的记录

swift - 我怎么知道如何遵守 Swift 中的特定协议(protocol)?

javascript - 在 JavaScript 中使用 for 循环组合两个数组

javascript - 将 javascript 数组插入到另一个数组中

java - 打印空数组

ios - 协议(protocol)类型不能符合协议(protocol),因为只有具体类型才能符合协议(protocol)