Swift - 将协议数组向上转换为超级协议数组会导致错误

2023-12-08

在 Swift 中,我注意到我可以向上转换一个符合名为的协议的对象,比方说SubProtocol到另一个称为SuperProtocol这是一个超级协议SubProtocol。但我不能对协议数组做同样的事情。这是我在 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 MammalLocomotion {
    func legs() -> Int
}

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

protocol CowLocomotion : MammalLocomotion {

}

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

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

class Mammal : MammalLocomotion {

}

class Cow : Mammal, CowLocomotion {

}

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

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

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

但现在我们来投射cow to Mammal:

let cowAsMammal : Mammal = cow

cowAsMammal.legs() // 2

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

解决方法

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

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

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

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

有关协议如何继承的更多信息,请参阅今年 WWDC 的面向协议的 Swift 编程会议 -文字记录在这里.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Swift - 将协议数组向上转换为超级协议数组会导致错误 的相关文章

随机推荐

  • socket.on 事件被多次触发

    var express require express var app express var server app listen 3000 var replyFromBot app use express static public va
  • 使用 ==> fprintf 时出错 未为“sym”输入定义函数

    这是我的 MATLAB 代码 函数 trapezoidal 是单独定义的 并且工作正常 syms x f 10 2 x 6 x 2 5 x 4 a 0 b 2 ans 3points trapezoidal f a b 3 ans 5poi
  • C语言计算1+(1/2!)+…+(1/n!) n个数的和

    就像标题所说 我如何计算n个数字的总和 1 1 2 1 n 我已经得到了调和级数的代码 include
  • 将 libgdx 添​​加到 Android 本机应用程序

    我有一个简单的儿童应用程序 可以教授颜色 数字等内容 我目前正在开发该应用程序 它使用我认为的 标准 android java 编程 单个 Xml java 类 我还有一个简单的恐龙游戏 它使用 libgdx 跳过字母 我的问题是 我希望将
  • OpenSSL .NET C# 包装器 X509 认证

    您好 我在我的 c 项目中使用 OpenSSL NET 包装器 我想生成 X509 认证 但我不太了解程序 它应该包含什么 什么参数 等等 这是我的代码 我在查看一些测试后做到了 OpenSSL X509 X509Certificate x
  • 设置 PopupWindow 具有最大高度

    我在 PopupWindow 内膨胀 ListView 我希望弹出窗口的行为如下 当列表视图的高度 当列表的高度 gt x 列表视图可滚动 时 设置弹出窗口的高度 x 弹出窗口由附加到 EditText 的 TextWatcher 显示 因
  • 将字符串拆分为标记并将它们保存在数组中

    如何将字符串拆分为标记 然后将它们保存在数组中 具体来说 我有一个字符串 abc qwe jkh 我想分开 然后将标记保存到数组中 输出将是这样的 array 0 abc array 1 qwe array 2 jkh 请帮我 includ
  • If Let 错误 - 条件绑定的初始化程序必须具有可选类型,而不是“[NSObject : AnyObject]”

    这是我的代码 PFUser logInWithUsernameInBackground email password password user PFUser error NSError gt Void in if user nil Pus
  • std::map 放置 gcc 4.8.2

    我正在尝试使用 std map 的 emplace 函数 但它似乎没有实现 但我读到它是在 4 8 中实现的 以下代码 std map
  • 其他库中类型之间的 C++ 转换运算符

    为了方便起见 我希望能够在其他库中定义的两种类型之间进行转换 具体来说 QString来自 Qt 库和UnicodeString来自 ICU 库 现在 我已经在项目命名空间中创建了实用函数 namespace MyProject const
  • 堆栈面板的水平滚动不起作用

    我尝试创建一个可滚动的水平 StackPanel 但我没有成功 目前我的 StackPanel 带有auto宽度 问题可能就在这里 包含一些项目 例如grids 现在 如果我的所有网格在 StackPanel 中都不可见 宽度太短 我将无法
  • 计算输入中的行数、单词数和字符数

    现在我正在阅读一本关于 C 的书 并且在书中遇到了一个我无法开始工作的示例 include
  • Shell 脚本中的十六进制到十进制

    有人可以帮我在 shell 脚本中将十六进制数转换为十进制数吗 例如 我想转换十六进制数bfca3000使用 shell 脚本转换为十进制 我基本上想要两个十六进制数的差 我的代码是 var3 echo ibase 16 var1 bc v
  • 带有额外字段的 Django Rest Framework 用户注册

    我正在尝试使用 DRF 来允许用户通过我的 API 创建新的用户帐户 我有一些可能与正常情况不同的要求 成功创建后 需要使用 DRF 的令牌功能返回用户令牌 所有 POST 字段都需要验证 我希望能够发布将存储在配置文件模型中的用户电话号码
  • 如何用文本文件项填充组合框!

    我有一个文本文件 其中包含以下类型的项目 wett45456 4556 45657 898 tyu5878 4566 7989 55565 现在我有一个 Windows 窗体 该窗体上有一个组合框 现在我想用每行的第一项填充组合框wett4
  • @font-face 和 Header 设置 Access-Control-Allow-Origin "*"

    我使用了以下规则来允许我们的静态域托管字体 但是当启用浏览器缓存时 我遇到了浏览器 firefox safari 不使用字体的问题
  • devise - 无法在 Rails 视图中显示登录或注销

    我现在正在使用 devise 进行基本身份验证 当我去localhost 3000 users sign in我将能够登录 或者如果我登录后前往那里 我将收到相应的消息 您已经登录 然而 user signed in 始终评估为 false
  • Spring xml ioc 相对于 Java 实例化有什么好处? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 好吧 这个问题会得到很多反
  • 将 Pandoc 与 Swift 结合使用

    我正在尝试使用 Pandoc 将 LaTeX 转换为 Markdown 我需要创建一个文件 然后运行 pandoc 终端命令 问题是我创建的文件不在我运行终端命令的同一目录中 我尝试使用 shell cd 但它不会将您移动到用户的文件夹 有
  • Swift - 将协议数组向上转换为超级协议数组会导致错误

    在 Swift 中 我注意到我可以向上转换一个符合名为的协议的对象 比方说SubProtocol到另一个称为SuperProtocol这是一个超级协议SubProtocol 但我不能对协议数组做同样的事情 这是我在 Playground 中