函数在参数类型上是逆变的,在 return 类型上是协变的
Functions are contravariant in their argument types and co-variant in their return types
在发布这个问题之前我已经阅读了 this and this 个答案,但是我对这个主题的理解仍然有点不清楚,如下所述:
我理解协变和逆变独立的意思。
如果我有 类 如下:
class Car {}
class SportsCar extends Car {}
class Ferrari extends SportsCar {}
并且:
object covar extends App {
// Test 1: Works as expected
def test1( arg: SportsCar => SportsCar ) = {
new SportsCar
}
def foo1(arg: Car): Ferrari = { new Ferrari }
def foo2(arg: SportsCar): Car = { new Ferrari }
def foo3(arg: Ferrari): Ferrari = { new Ferrari }
test1(foo1) // compiles
test1(foo2) // Fails due to wrong return type - violates return type is covariant
test1(foo3) // Fails due to wrong parameter type - violates param type is contravariant
// Test 2: Confused - why can I call test2 succesfully with ferrari
// as the parameter, and unsuccesfully with a car as the parameter?
def test2(arg: SportsCar): SportsCar = {
new Ferrari
}
val car = new Car()
val sportsCar = new SportsCar()
val ferrari = new Ferrari()
val f1 = test2(ferrari) // compiles - why?
val f2 = test2(car) // fails - why?
}
如前所述,在测试2中,为什么以ferrari为参数调用test2可以成功,以car为参数调用却不能成功?
语句函数在它们的参数类型上是逆变的并且在它们的return类型上是协变的是否仅适用于作为参数传递的函数?我想我没有适当区分语句和我编写的 2 个测试。
那是因为您将 子类型 与 co/contravariance.
混用
在您的第一个示例中,您期望 Function1[-T, +R]
。在这种情况下,co/contravariance 的规则适用于 函数类型 。在第二个示例中,您遵循简单的子类型化规则。
在 test1
中,你传入 foo1
的地方是 Car => Ferrari
一切正常,因为 foo1
期望 Car
,但得到 [=16] =],这是一个Car
。由于子类型的性质,任何需要 Car
的方法都可以处理子类型。但是当我们单独谈论子类型时,这些规则不起作用。
如果我们用 foo1
和实际类型展开 test1
,也许会更清楚:
foo1:
- 参数类型:
- 预期:
Car
- 实际:
SportsCar
- Return 输入:
- 预期:
SportsCar
- 实际:
Ferrari
一切都很好。
在 test2
中,规则发生了变化。如果我期望 SportsCar
你传入 任何汽车 ,那么我不能再依赖于输入是跑车并且一切都坏了,但是如果你传入法拉利居然是跑车,什么都好
再次,让我们排列类型:
测试2:
- 第一种情况:
- 参数类型:
- 预期:
SportsCar
- 实际:
Ferrari
(子类型)
- 结果:大家都很开心
- 第二种情况:
- 参数类型:
- 预期:
SportsCar
- 实际:
Car
- 结果:错误。我们不确定汽车是否是真正的跑车。
在发布这个问题之前我已经阅读了 this and this 个答案,但是我对这个主题的理解仍然有点不清楚,如下所述:
我理解协变和逆变独立的意思。
如果我有 类 如下:
class Car {}
class SportsCar extends Car {}
class Ferrari extends SportsCar {}
并且:
object covar extends App {
// Test 1: Works as expected
def test1( arg: SportsCar => SportsCar ) = {
new SportsCar
}
def foo1(arg: Car): Ferrari = { new Ferrari }
def foo2(arg: SportsCar): Car = { new Ferrari }
def foo3(arg: Ferrari): Ferrari = { new Ferrari }
test1(foo1) // compiles
test1(foo2) // Fails due to wrong return type - violates return type is covariant
test1(foo3) // Fails due to wrong parameter type - violates param type is contravariant
// Test 2: Confused - why can I call test2 succesfully with ferrari
// as the parameter, and unsuccesfully with a car as the parameter?
def test2(arg: SportsCar): SportsCar = {
new Ferrari
}
val car = new Car()
val sportsCar = new SportsCar()
val ferrari = new Ferrari()
val f1 = test2(ferrari) // compiles - why?
val f2 = test2(car) // fails - why?
}
如前所述,在测试2中,为什么以ferrari为参数调用test2可以成功,以car为参数调用却不能成功?
语句函数在它们的参数类型上是逆变的并且在它们的return类型上是协变的是否仅适用于作为参数传递的函数?我想我没有适当区分语句和我编写的 2 个测试。
那是因为您将 子类型 与 co/contravariance.
混用在您的第一个示例中,您期望 Function1[-T, +R]
。在这种情况下,co/contravariance 的规则适用于 函数类型 。在第二个示例中,您遵循简单的子类型化规则。
在 test1
中,你传入 foo1
的地方是 Car => Ferrari
一切正常,因为 foo1
期望 Car
,但得到 [=16] =],这是一个Car
。由于子类型的性质,任何需要 Car
的方法都可以处理子类型。但是当我们单独谈论子类型时,这些规则不起作用。
如果我们用 foo1
和实际类型展开 test1
,也许会更清楚:
foo1:
- 参数类型:
- 预期:
Car
- 实际:
SportsCar
- 预期:
- Return 输入:
- 预期:
SportsCar
- 实际:
Ferrari
- 预期:
一切都很好。
在 test2
中,规则发生了变化。如果我期望 SportsCar
你传入 任何汽车 ,那么我不能再依赖于输入是跑车并且一切都坏了,但是如果你传入法拉利居然是跑车,什么都好
再次,让我们排列类型:
测试2:
- 第一种情况:
- 参数类型:
- 预期:
SportsCar
- 实际:
Ferrari
(子类型)
- 预期:
- 结果:大家都很开心
- 参数类型:
- 第二种情况:
- 参数类型:
- 预期:
SportsCar
- 实际:
Car
- 预期:
- 结果:错误。我们不确定汽车是否是真正的跑车。
- 参数类型: