参数类型不是反变体吗?

时间:2012-11-10 12:11:01

标签: java scala

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

  

函数在其参数类型和共变体中是逆变的   他们的回报类型

例如,在Java中,让Dog扩展Animal。让一个函数成为:

void getSomething(Animal a){

我将函数调用为

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

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

4 个答案:

答案 0 :(得分:30)

这是how functions are defined in Scala

trait Function1 [-T1, +R]  extends AnyRef

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

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

此外,由于结果类型的协方差,您可以提供Dog => Dog类型的功能(您可以使用更窄的类型)。

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

答案 1 :(得分:4)

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

答案 2 :(得分:1)

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

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

public int secondOrderFunction(Function<? super Integer, ? extends Number> fn) {
     ....
}

(假设一个适当声明的接口Function<P, R>P代表参数类型,R代表返回类型)。当然,由于此代码掌握在客户端手中,并且根本不是特定于函数,因此关于param类型/返回类型方差的声明不适用于Java的任何语言特性。它仅适用于更广泛的意义,属于函数的性质

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

答案 3 :(得分:0)

我认为关于将 Dog 转换为 Animal 的原始问题已经阐明,但是可能有意思的是注意到在函数中定义函数是变量是有原因的它的参数和返回类型的协变。假设您有两个功能:

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

在我们谈论函数时,您期望functions composition属于您的原始操作。确实,您可以组成f和g( g o f )并获得一个函数:

val h: Vertebrate => Primate = f andThen g

但是我可以将g替换为子类型:

val gChild: Animal => Primate

不破坏可组合性。 gChildg的子类型,恰好是因为我们在其参数中定义了函数矛盾。结论是,如果您想捕获并保留 functionscomposability 的思想,则可以看到必须以这种方式定义函数。 您可以找到更多详细信息和少量图形,这些图形应该有助于消化该主题here