Дисперсионная функция карты списка Scala
У меня есть вопрос, который меня беспокоит. Списки в Scala являются ковариантными (List[+A]
)
Допустим, у нас есть эти классы:
class A
class B extends A
map
функция List[B]
берет на себя функцию f: B => C
Но я также могу использовать f: A => C
который является подклассом f: B => C
и это полностью имеет смысл.
То, что я в настоящее время смущен тем, что map
Функция должна принимать только те функции, которые являются суперклассами исходной функции (поскольку функции противоречивы по своим аргументам), что не применимо в приведенном мной примере.
Я знаю, что что-то не так с моей логикой, и я бы хотел просветить.
2 ответа
Ваша ошибка заключается в предположении, что map(f: A => C)
должны принимать только те функции, которые являются суперклассами A => C
,
Хотя на самом деле, map
будет принимать любую функцию, которая является подклассом A => C
,
В Scala параметр функции всегда может быть подклассом требуемого типа.
Ковариация A
в List[A]
только говорит вам, что везде, где List[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
с, так что дисперсия List
здесь не имеет значения Здесь вас смущает то, что есть некоторая подтипировка, которую следует учитывать: map
принимает аргумент (A => C
в данном случае, но это не очень актуально), и, как обычно, с методами и функциями, вы всегда можете заменить его аргумент чем-либо, что является его подтипом. В вашем конкретном случае, любой AcceptedType
будет хорошо, пока AcceptedType <: Function1[A,C]
, Дисперсия, которая имеет значение здесь Function
х, не List
"S.