为什么我不能在Swift中将(String)-> String转换为(Any)-> Any?

时间:2019-06-06 07:00:41

标签: swift types casting

由于Any可以容纳任何类型,而String是较低类型,为什么不能将(String) -> String函数转换为(Any) -> Any函数?

func lower(_ s: String) -> String {
    return s.lowercased()
}

func upper(_ s: String) -> String {
    return s.uppercased()
}

func foo(_ s: @escaping (Any) -> Any) -> (Any) -> Any {
    return s
}


let f = foo(lower as! (Any) -> Any)  // error: Execution was interrupted, reason: signal SIGABRT.

f("ABC")

1 个答案:

答案 0 :(得分:1)

  

由于Any可以容纳任何类型,而String是较低类型,为什么不能将(String) -> String函数转换为(Any) -> Any函数?

因为两者彼此无关。

是的,确实是String <:Any。这意味着您可以将String强制转换为Any。但是,您并没有尝试将String强制转换为Any。您正在尝试将(String) -> String强制转换为(Any) -> AnyString的两个完全不同类型的Any(String) -> StringString不同,并且(Any) -> AnyAny不同,因此绝对没有理由为StringAny还应该自动保持(String) -> String(Any) -> Any的位置……并且,您发现,该关系确实保持了 的状态。

简单的答案是:函数的参数类型是互变的,而返回类型是协变的。因此,(String) -> String(String) -> Any子类型(Any) -> String超类型,它既不是{<1>}的子类型也不是其超类型。 (Any) -> Any

在1970年代初期,一位名为Barbara Liskov的计算机科学家发明了一种以行为替代的新思维方式,现在我们将其称为Liskov替代原理(LSP)。 em>。我们可以使用LSP来准确解释为什么函数的参数类型是互变量的,而返回类型是协变量的。 (注意:即使在Liskov之前,这也是众所周知的,但是LSP为我们提供了一个很好的解释原因的方法。)

Barbara Liskov's Substitution Principle告诉我们类型S是类型T的子类型 IFF T的任何实例都可以替换为一个实例S的值,而不会更改程序的可观察的所需属性。

让我们采用一个简单的泛型类型,一个函数。一个函数有两个类型参数,一个用于输入,一个用于输出。 (我们在这里保持简单。)(A) -> B是一个接受类型A的参数并返回类型B的结果的函数。

现在,我们经历几个场景。我有一些操作 O ,希望使用从FruitMammal s的函数(是的,我知道,令人兴奋的原始示例!)LSP表示我应该还可以传入该函数的子类型,并且一切仍然应该正常工作。假设A中的函数是协变的。然后,我应该也可以将功能从Apple传递到Mammal。但是,当 O Orange传递给函数时会发生什么?那应该被允许! O 能够将Orange传递给(Fruit) -> Mammal,因为OrangeFruit的子类型。但是,来自Apple的函数不知道如何处理Orange的函数,因此它会崩溃。 LSP表示它应该工作,但这意味着我们可以得出的唯一结论是我们的假设是错误的:(Apple) -> Mammal不是(Fruit)-> Mammal的子类型,换句话说,函数在{中不是协变的{1}}。

如果它是反变量呢?如果我们将A传递给 O ,该怎么办?好吧, O 再次尝试通过(Food) -> Mammal,它的工作原理是:OrangeOrange,所以Food知道如何处理{{ 1}} s。现在我们可以得出结论,函数在输入中是 contravariant ,即您可以传递采用更通用类型的函数作为其输入,以代替采用更受限类型的函数,并且一切正常很好。

现在让我们看一下函数的返回类型。如果(Food) -> Mammal)中的函数与Orange中的函数一样,该怎么办?我们将B传递给 O 。根据LSP,如果我们是对的,并且函数的返回类型是互斥的,则不会发生任何不良情况。不幸的是, O 对函数的结果调用A方法,但是函数只是返回了一个(Fruit) -> Animal。哎呀。因此,函数的返回类型不能互变。

OTOH,如果我们通过getMilk,会发生什么?一切仍然有效! O 给返回的母牛打电话Chicken,它的确提供了牛奶。因此,函数似乎在其输出中是协变的。

这是适用于方差的一般规则:

  • 在LSP的意义上,使用(Fruit) -> Cow中的getMilk 协变量 IFF C<A>是安全的作为输出。
  • (在LSP的意义上)使用A中的A 相反是很安全的。 IFF C<A> 作为输入。
  • 如果A可以用作输入或输出,则A 必须A中是不变的,否则结果不安全。
相关问题