问题描述
我知道 (Int) ->Void
不能被类型转换为 (Any) ->无效
:
I know that (Int) -> Void
can't be typecasted to (Any) -> Void
:
let intHandler: (Int) -> Void = { i in
print(i)
}
var anyHandler: (Any) -> Void = intHandler <<<< ERROR
这给出了:
错误:无法将类型 '(Int) -> Void' 的值转换为指定类型'(任何)-> 无效'
error: cannot convert value of type '(Int) -> Void' to specified type '(Any) -> Void'
<小时>
问题:但我不知道为什么会这样?
Question: But I don't know why this work?
let intResolver: ((Int) -> Void) -> Void = { f in
f(5)
}
let stringResolver: ((String) -> Void) -> Void = { f in
f("wth")
}
var anyResolver: ((Any) -> Void) -> Void = intResolver
我弄乱了返回类型,但它仍然有效...:
I messed around with the return type and it still works...:
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)
抱歉,如果之前有人问过这个问题.我还没有找到这种问题,也许我不知道这里的关键字.请赐教!
Sorry if this is asked before. I couldn't find this kind of question yet, maybe I don't know the keyword here. Please enlighten me!
如果您想尝试:https://iswift.org/playground?wZgwi3&v=3
推荐答案
这都是关于 variance 和 Swift 闭包.
It's all about variance and Swift closures.
Swift 在闭包返回类型方面是协变的,在其参数方面是逆变的.这使得具有相同返回类型或更具体的返回类型以及相同或不太具体的参数的闭包是兼容的.
Swift is covariant in respect to closure return type, and contra-variant in respect to its arguments. This makes closures having the same return type or a more specific one, and same arguments or less specific, to be compatible.
因此 (Arg1) ->Res1
可以分配给 (Arg2) ->Res2
if Res1: Res2
and Arg2: Arg1
.
Thus (Arg1) -> Res1
can be assigned to (Arg2) -> Res2
if Res1: Res2
and Arg2: Arg1
.
为了表达这一点,让我们稍微调整一下第一个闭包:
To express this, let's tweak a little bit the first closure:
import Foundation
let nsErrorHandler: (CustomStringConvertible) -> NSError = { _ in
return NSError(domain: "", code: 0, userInfo: nil)
}
var anyHandler: (Int) -> Error = nsErrorHandler
上面的代码之所以有效,是因为Int
符合CustomStringConvertible
,而NSError
符合Error
.Any
也可以代替 Error
工作,因为它更通用.
The above code works because Int
conforms to CustomStringConvertible
, while NSError
conforms to Error
. Any
would've also work instead of Error
as it's even more generic.
现在我们已经确定了这一点,让我们看看在您的两个代码块中发生了什么.
Now that we established that, let's see what happens in your two blocks of code.
第一个块尝试将更具体的参数闭包分配给不太具体的闭包,这不遵循方差规则,因此无法编译.
The first block tries to assign a more specific argument closure to a less specific one, and this doesn't follow the variance rules, thus it doesn't compile.
第二个代码块怎么样?我们处于与第一个块中类似的场景:带有一个参数的闭包.
How about the second block of code? We are in a similar scenario as in the first block: closures with one argument.
- 我们知道
String
或Void
比Any
更具体,因此我们可以将其用作返回值 (Int) ->Void
比(Any) -> 更具体.Void
(闭包变化规则),所以我们可以用它作为参数
- we know that
String
, orVoid
, is more specific thatAny
, so we can use it as return value (Int) -> Void
is more specific than(Any) -> Void
(closure variance rules), so we can use it as argument
遵守闭包变化,因此 intResolver
和 stringResolver
是 anyResolver
的兼容匹配.这听起来有点违反直觉,但仍然遵循编译规则,并且允许赋值.
The closure variance is respected, thus intResolver
and stringResolver
are a compatible match for anyResolver
. This sounds a little bit counter-intuitive, but still the compile rules are followed, and this allows the assignment.
然而,如果我们想使用闭包作为泛型参数,事情就变得复杂了,方差规则不再适用,这是因为 Swift 泛型(除了少数例外)在其类型方面是不变的:MyGenericType<即使
分配给 B: A
也无法将 B>MyGenericType
.例外是标准库结构,例如 Optional
和 Array
.
Things complicate however if we want to use closures as generic arguments, the variance rules no longer apply, and this due to the fact that Swift generics (with few exceptions) are invariant in respect to their type: MyGenericType<B>
can't be assigned to MyGenericType<A>
even if B: A
. The exceptions are standard library structs, like Optional
and Array
.
这篇关于类型转换/多态如何在 Swift 中使用这种嵌套的闭包类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!