我有一个问题一直困扰着我。Scala 中的列表是协变的 ( List[+A]
)
假设我们有这些类:
class A
class B extends A
map
函数取List[B]
函数f: B => C
但我也可以使用 af: A => C
它的子类,f: B => C
它完全有意义。
我目前感到困惑的是,该map
函数应该只接受作为原始函数超类的函数(因为函数的参数是逆变的),这不适用于我给出的示例。
我知道我的逻辑有问题,我想开导。
我有一个问题一直困扰着我。Scala 中的列表是协变的 ( List[+A]
)
假设我们有这些类:
class A
class B extends A
map
函数取List[B]
函数f: B => C
但我也可以使用 af: A => C
它的子类,f: B => C
它完全有意义。
我目前感到困惑的是,该map
函数应该只接受作为原始函数超类的函数(因为函数的参数是逆变的),这不适用于我给出的示例。
我知道我的逻辑有问题,我想开导。
您的错误在于假设map(f: A => C)
应该只接受属于A => C
.
而实际上,map
将接受任何属于A => C
.
在 Scala 中,函数参数始终可以是所需类型的子类。
A
in的协方差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)
我希望这有帮助。
正如您已经怀疑的那样,您在这里混淆了一些东西。
一方面,你有一个List[+A]
,它告诉我们一些关于 和 之间的关系,给定List[A]
和之间的List[B]
关系。协变的事实只是意味着当,正如你已经知道的那样。A
B
List
A
List[B] <: List[A]
B <: A
另一方面,List
公开一种map
更改其“内容”的方法。这种方法并不真正关心List[A]
,而只关心A
s,因此这里的方差List
无关紧要。让你感到困惑的是,确实有一些子类型需要考虑:map
接受一个参数(A => C
在这种情况下是 a,但它并不真正相关),并且像往常一样使用方法和函数,你总是可以替换它的任何是它的子类型的参数。在您的具体情况下,任何AcceptedType
都可以,只要AcceptedType <: Function1[A,C]
. 这里重要的方差是Function
's,而不是List
's。