由于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")
答案 0 :(得分:1)
由于
Any
可以容纳任何类型,而String是较低类型,为什么不能将(String) -> String
函数转换为(Any) -> Any
函数?
因为两者彼此无关。
是的,确实是String
<:Any
。这意味着您可以将String
强制转换为Any
。但是,您并没有尝试将String
强制转换为Any
。您正在尝试将(String) -> String
强制转换为(Any) -> Any
和String
的两个完全不同类型的Any
。 (String) -> String
与String
不同,并且(Any) -> Any
与Any
不同,因此绝对没有理由为String
和Any
还应该自动保持(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 ,希望使用从Fruit
到Mammal
s的函数(是的,我知道,令人兴奋的原始示例!)LSP表示我应该还可以传入该函数的子类型,并且一切仍然应该正常工作。假设A
中的函数是协变的。然后,我应该也可以将功能从Apple
传递到Mammal
。但是,当 O 将Orange
传递给函数时会发生什么?那应该被允许! O 能够将Orange
传递给(Fruit) -> Mammal
,因为Orange
是Fruit
的子类型。但是,来自Apple
的函数不知道如何处理Orange
的函数,因此它会崩溃。 LSP表示它应该工作,但这意味着我们可以得出的唯一结论是我们的假设是错误的:(Apple) -> Mammal
不是(Fruit)-> Mammal
的子类型,换句话说,函数在{中不是协变的{1}}。
如果它是反变量呢?如果我们将A
传递给 O ,该怎么办?好吧, O 再次尝试通过(Food) -> Mammal
,它的工作原理是:Orange
是Orange
,所以Food
知道如何处理{{ 1}} s。现在我们可以得出结论,函数在输入中是 contravariant ,即您可以传递采用更通用类型的函数作为其输入,以代替采用更受限类型的函数,并且一切正常很好。
现在让我们看一下函数的返回类型。如果(Food) -> Mammal)
中的函数与Orange
中的函数一样,该怎么办?我们将B
传递给 O 。根据LSP,如果我们是对的,并且函数的返回类型是互斥的,则不会发生任何不良情况。不幸的是, O 对函数的结果调用A
方法,但是函数只是返回了一个(Fruit) -> Animal
。哎呀。因此,函数的返回类型不能互变。
OTOH,如果我们通过getMilk
,会发生什么?一切仍然有效! O 给返回的母牛打电话Chicken
,它的确提供了牛奶。因此,函数似乎在其输出中是协变的。
这是适用于方差的一般规则:
(Fruit) -> Cow
中的getMilk
协变量 IFF C<A>
是安全的仅作为输出。A
中的A
相反是很安全的。 IFF C<A>
仅作为输入。A
可以用作输入或输出,则A
必须在A
中是不变的,否则结果不安全。