参数类型不是反变体吗?

我理解术语协方差和反方差。 但有一件小事我无法理解。 在课程“Scala中的function编程”课程中,Martin Ordersky提到:

函数在其参数类型中是逆变的,在它们的返回类型中是共变量

因此,例如在Java中,让Dog扩展Animal 。 让一个函数是:

 void getSomething(Animal a){ 

我有函数调用

 Dog d = new Dog(); getSomething(d) 

所以基本上发生的事情是Animal a = d 。 根据维基的协方差是“将范围扩大到狭窄”。 而且我们正在从狗转变为动物。 SO不是参数类型协变而不是逆变?

这是在Scala中定义函数的方式 :

 trait Function1 [-T1, +R] extends AnyRef 

在英语中,参数T1是逆变的,结果类型R是协变的。 这是什么意思?

当某段代码需要Dog => Animal类型的函数时,你可以提供Animal => Animal类型的函数,这要归功于参数的逆变(你可以使用更宽泛的类型)。

你也可以提供Dog => Dog类型的function,这要归功于结果类型的协方差(你可以使用更窄的类型)。

这实际上是有道理的:有人想要一个function将狗变成任何动物。 您可以提供转换任何动物(包括狗)的function。 此外,您的function只能返回狗,但狗仍然是动物。

Dog转换为Animal正在将范围转换为更宽,因此它不是协方差。

我记得当我在2007年阅读Scala书时,被这句话弄糊涂了.Martin把它当作一个语言特征来表达,但在那句话中他只说了一般关于function的事实。 具体来说,Scala只是通过常规特征对事实进行建模。 由于Scala具有声明站点方差,因此表达这些语义对于语言来说是很自然的。

另一方面,Java Generics仅支持使用站点方差,因此最接近Java的函数类型的共同/逆转是在每个使用站点手动编码:

 public int secondOrderFunction(Function fn) { .... } 

(假设适当声明的接口FunctionP代表参数类型, R代表返回类型)。 当然,由于此代码掌握在客户端手中,而不是特定于函数,因此有关param类型/返回类型方差的声明不适用于Java的任何语言特性。 它仅适用于更广泛的意义,涉及function的性质

Java 8将引入闭包,这意味着一流的function,但是,根据下面的Jörg的评论,实现将不包括完全成熟的函数类型。

我认为关于将Dog转换为Animal的原始问题已经得到了澄清,但可能有意思的是,有一个原因使得函数在其参数中被定义为逆变,并且在其返回类型中具有协变性。 假设您有两个function:

val f: Vertebrate => Mammal = ??? val g: Mammal => Primate = ???

在我们讨论函数时,您会希望函数组合在您的原始操作中。 实际上,你可以编写f和g( gof )并获得一个函数:

val h: Vertebrate => Primate = f andThen g

但我可以用子类型替换g

val gChild: Animal => Primate

不破坏可组合性。 gChildg的子类型,因为我们在其参数中定义了函数逆变量。 作为结论,您可以看到,如果要捕获并保留函数可组合性的概念,则必须以这种方式定义函数 。 您可以在此处找到有助于消化此主题的更多详细信息和一些图形