2

我有一个问题一直困扰着我。Scala 中的列表是协变的 ( List[+A])

假设我们有这些类:

class A  
class B extends A

map函数取List[B]函数f: B => C

但我也可以使用 af: A => C 它的子类,f: B => C
它完全有意义。

我目前感到困惑的是,该map函数应该只接受作为原始函数超类的函数(因为函数的参数是逆变的),这不适用于我给出的示例。

我知道我的逻辑有问题,我想开导。

4

2 回答 2

3

您的错误在于假设map(f: A => C)应该只接受属于A => C.

而实际上,map将接受任何属于A => C.

在 Scala 中,函数参数始终可以是所需类型的子类。

Ain的协方差List[A]只告诉你,只要List[A]需要 a,你就可以提供 a List[B],只要B <: A

或者,用更简单的话来说:List[B]可以将其视为List[A].

我编译了一个小例子来解释这两种行为:

class A  
class B extends A

// this means: B <: A

val listA: List[A] = List()
val listB: List[B] = List()

// Example 1: List[B] <: List[A]
// Note: Here the List[+T] is our parameter! (Covariance!)

def printListA(list: List[A]): Unit = println(list)

printListA(listA)
printListA(listB)

// Example 2: Function[A, _] <: Function[B, _]
// Note: Here a Function1[-T, +R] is our parameter (Contravariance!)

class C

def fooA(a: A): C = ???
def fooB(b: B): C = ???

listB.map(fooB)
listB.map(fooA)

试试看!

我希望这有帮助。

于 2018-12-07T10:03:40.710 回答
0

正如您已经怀疑的那样,您在这里混淆了一些东西。

一方面,你有一个List[+A],它告诉我们一些关于 和 之间的关系,给定List[A]和之间的List[B]关系。协变的事实只是意味着当,正如你已经知道的那样。ABListAList[B] <: List[A]B <: A

另一方面,List公开一种map更改其“内容”的方法。这种方法并不真正关心List[A],而只关心As,因此这里的方差List无关紧要。让你感到困惑的是,确实有一些子类型需要考虑:map接受一个参数(A => C在这种情况下是 a,但它并不真正相关),并且像往常一样使用方法和函数,你总是可以替换它的任何是它的子类型的参数。在您的具体情况下,任何AcceptedType都可以,只要AcceptedType <: Function1[A,C]. 这里重要的方差是Function's,而不是List's。

于 2018-12-07T09:56:59.810 回答