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

时间:2018-02-18 05:32:42

标签: swift casting polymorphism

我知道(Int) -> Void不能被(Any) -> Void类型化:

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

这给出了:

  

错误:无法转换类型&#39;(Int) - &gt;的值虚空&#39;到指定的类型   &#39;(任何) - &gt;空隙&#39;

问题:但我不知道为什么会这样做?

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

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

var anyResolver: ((Any) -> Void) -> Void = intResolver

我用返回类型搞砸了它仍然有用......:

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)

很抱歉,如果之前有人问这个问题。我还没有找到这样的问题,也许我不知道这里的关键字。 请赐教!

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

2 个答案:

答案 0 :(得分:2)

所有关于variance和Swift的关闭。

Swift在闭包返回类型方面是协变的,在其参数方面是反变量。这使得闭包具有相同的返回类型或更具体的闭包,相同的参数或更少的特定,是兼容的。

因此,如果(Arg1) -> Res1(Arg2) -> Res2Res1: Res2可以分配到Arg2: Arg1

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

import Foundation

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

上述代码有效,因为Int符合CustomStringConvertible,而NSError符合Error。 <{1}}也会工作,而不是Any,因为它更通用。

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

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

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

  • 我们知道ErrorString更具体Void,因此我们可以将其用作返回值
  • Any(Int) -> Void(闭包差异规则)更具体,因此我们可以将其用作参数

关系差异得到尊重,因此(Any) -> VoidintResolverstringResolver的兼容匹配。这听起来有点违反直觉,但仍然遵循编译规则,这允许分配。

事情变得复杂但是如果我们想要使用闭包作为泛型参数,则方差规则不再适用,这是因为Swift泛型(少数例外)在其类型方面是不变的:anyResolver即使MyGenericType<B>,也无法分配到MyGenericType<A>。例外情况是标准库结构,例如B: AOptional

答案 1 :(得分:1)

首先,让我们考虑为什么你的第一个例子是非法的:

let intHandler: (Int) -> Void = { i in
    print(i)
}
var anyHandler: (Any) -> Void = intHandler
// error: Cannot convert value of type '(Int) -> Void' to specified type '(Any) -> Void'

(Any) -> Void是一个可以处理任何输入的函数; (Int) -> Void是一个只能 处理Int输入的函数。因此,我们不能将Int - 将函数视为可以处理任何的函数,因为它不能。如果我们使用anyHandler调用String

,该怎么办?

另一种方式呢?这是合法的:

let anyHandler: (Any) -> Void = { i in
  print(i)
}
var intHandler: (Int) -> Void = anyHandler

为什么呢?因为我们可以将处理任何的函数视为可以处理Int的函数,因为如果它可以处理任何事情,根据定义它必须能够处理{{1 }}

所以我们已经确定我们可以将Int视为(Any) -> Void。让我们看看你的第二个例子:

(Int) -> Void

为什么我们可以将let intResolver: ((Int) -> Void) -> Void = { f in f(5) } var anyResolver: ((Any) -> Void) -> Void = intResolver 视为((Int) -> Void) -> Void?换句话说,为什么在调用((Any) -> Void) -> Void时我们可以将anyResolver参数转发到(Any) -> Void参数?好吧,正如我们已经发现的那样,我们可以将(Int) -> Void视为(Any) -> Void,因此它是合法的。

同样的逻辑适用于(Int) -> Void的示例:

((String) -> Void) -> Void

在致电let stringResolver: ((String) -> Void) -> Void = { f in f("wth") } var anyResolver: ((Any) -> Void) -> Void = stringResolver 时,我们可以将anyResolver传递给它,然后传递到(Any) -> Void,其中stringResolver。并且可以处理任何事物的函数也是处理字符串的函数,因此它是合法的。

使用返回类型工作:

(String) -> Void

由于let intResolver: ((Int) -> Void) -> String = { f in f(5) return "I want to return some string here." } var anyResolver: ((Any) -> Void) -> Any = intResolver 表示返回intResolverString表示返回anyResolver;一个字符串 Any,所以它是合法的。