斯威夫特 4.1 更新
这是一个已修复的错误在此拉取请求中 https://github.com/apple/swift/pull/13138,这将进入 Swift 4.1 的发行版。您的代码现在可以在 4.1 快照中按预期进行编译。
Swift 4.1 之前的版本
这看起来像是你对编译器的过度拉伸。
它可以处理从子类型元素数组到超类型元素数组的转换,例如[A]
to [SomeProtocol]
– 这就是协方差。值得注意的是,数组在这里一直是一种边缘情况,因为任意泛型都是不变的。某些集合,例如Array
, just 得到编译器的特殊对待 https://stackoverflow.com/q/37188580/2976878允许协方差。
它可以处理具有超类型参数的函数到具有子类型参数的函数的转换,例如(SomeProtocol) -> Void
to (A) -> Void
——这就是逆变。
然而,目前看来它不能一次性完成这两件事(但实际上它应该能够;随意提交错误 https://bugs.swift.org).
就其价值而言,这与泛型无关,以下内容重现了相同的行为:
protocol SomeProtocol {}
class A : SomeProtocol {}
func f1(listener: (A) -> Void) {}
func f2(listener: ([A]) -> Void) {}
func f3(listener: () -> [SomeProtocol]) {}
func g() {
let l1: (SomeProtocol) -> Void = { _ in }
f1(listener: l1) // NO ERROR
let l2: ([SomeProtocol]) -> Void = { _ in }
f2(listener: l2)
// COMPILER ERROR: Cannot convert value of type '([SomeProtocol]) -> Void' to
// expected argument type '([A]) -> Void'
// it's the same story for function return types
let l3: () -> [A] = { [] }
f3(listener: l3)
// COMPILER ERROR: Cannot convert value of type '() -> [A]' to
// expected argument type '() -> [SomeProtocol]'
}
在修复之前,这种情况下的一种解决方案是简单地使用闭包表达式作为两个函数类型之间的蹦床:
// converting a ([SomeProtocol]) -> Void to a ([A]) -> Void.
// compiler infers closure expression to be of type ([A]) -> Void, and in the
// implementation, $0 gets implicitly converted from [A] to [SomeProtocol].
f2(listener: { l2($0) })
// converting a () -> [A] to a () -> [SomeProtocol].
// compiler infers closure expression to be of type () -> [SomeProtocol], and in the
// implementation, the result of l3 gets implicitly converted from [A] to [SomeProtocol]
f3(listener: { l3() })
并且,应用于您的代码:
f2(ofType: A.self, listener: { l2($0) })
这是有效的,因为编译器推断闭包表达式的类型([T]?) -> Void
,可以传递给f2
。在闭包的实现中,编译器执行隐式转换$0
from [T]?
to [SomeProtocol]?
.
And, 正如多米尼克暗示的那样 https://stackoverflow.com/a/44991267/2976878,这个蹦床也可以作为额外的过载来完成f2
:
func f2<T : SomeProtocol>(ofType type: T.Type, listener: ([SomeProtocol]?) -> Void) {
// pass a closure expression of type ([T]?) -> Void to the original f2, we then
// deal with the conversion from [T]? to [SomeProtocol]? in the closure.
// (and by "we", I mean the compiler, implicitly)
f2(ofType: type, listener: { (arr: [T]?) in listener(arr) })
}
让您再次将其称为f2(ofType: A.self, listener: l2)
.