类型转换/多态如何在Swift中使用这种嵌套的闭包类型?

aun*_*nnn 5 polymorphism casting swift

我知道(Int) -> Void不能说是(Any) -> Void:

let intHandler: (Int) -> Void = { i in
    print(i)
}
var anyHandler: (Any) -> Void = intHandler <<<< ERROR
Run Code Online (Sandbox Code Playgroud)

这给出了:

错误:无法将类型'(Int) - > Void'的值转换为指定类型'(Any) - > Void'


问:但我不知道为什么这个工作?

let intResolver: ((Int) -> Void) -> Void = { f in
    f(5)
}

let stringResolver: ((String) -> Void) -> Void = { f in
    f("wth")
}

var anyResolver: ((Any) -> Void) -> Void = intResolver
Run Code Online (Sandbox Code Playgroud)

我弄乱了返回类型,它仍然可以工作......:

let intResolver: ((Int) -> Void) -> String = { f in
    f(5)
    return "I want to return some string here."
}

let stringResolver: ((String) -> Void) -> Void = { f in
    f("wth")
}

var anyResolver: ((Any) -> Void) -> Any = intResolver (or stringResolver)
Run Code Online (Sandbox Code Playgroud)

对不起,如果以前询问过.我还没有找到这样的问题,也许我不知道这里的关键字.请赐教!

如果你想尝试:https://iswift.org/playground?wZgwi3&v = 3

Cri*_*tik 4

这都是关于方差和 Swift 闭包的。

Swift 在闭包返回类型方面是协变的,在其参数方面是逆变的。这使得具有相同返回类型或更具体返回类型以及相同参数或不太具体的参数的闭包是兼容的。

因此(Arg1) -> Res1可以分配给(Arg2) -> Res2ifRes1: Res2和Arg2: Arg1。

为了表达这一点,让我们稍微调整一下第一个闭包:

import Foundation

let nsErrorHandler: (CustomStringConvertible) -> NSError = { _ in
    return NSError(domain: "", code: 0, userInfo: nil)
}
var anyHandler: (Int) -> Error = nsErrorHandler
Run Code Online (Sandbox Code Playgroud)

上面的代码之所以有效,是因为Int符合CustomStringConvertible,而NSError符合Error。Any也可以工作,而不是Error因为它更通用。

现在我们已经确定了这一点,让我们看看您的两个代码块中发生了什么。

第一个块尝试将更具体的参数闭包分配给不太具体的参数闭包,这不遵循方差规则,因此它无法编译。

第二个代码块怎么样?我们处于与第一个块类似的场景:带有一个参数的闭包。

  • 我们知道String, 或Void, 更具体Any,所以我们可以用它作为返回值
  • (Int) -> Void比(闭包方差规则)更具体(Any) -> Void,所以我们可以用它作为参数

闭合方差受到尊重,因此intResolver和stringResolver是 的兼容匹配anyResolver。这听起来有点违反直觉,但仍然遵循编译规则,并且允许赋值。

然而,如果我们想使用闭包作为泛型参数,事情就会变得复杂,方差规则不再适用,这是因为 Swift 泛型(除了少数例外)在其类型方面是不变的:MyGenericType<B>不能分配给MyGenericType<A>即使B: A。标准库结构除外,例如Optional和Array。