Какая разница между
[A <: B]
и
[+B]
в Scala?
Какая разница между
[A <: B]
и
[+B]
в Scala?
Q[A <: B]
означает, что класс Q
может принимать любой класс A
, который является подклассом B
.
Q[+B]
означает, что Q
может принимать любой класс, но если A
является подклассом B
, то Q[A]
считается подклассом Q[B]
.
Q[+A <: B]
означает, что класс Q
может принимать только подклассы B
, а также распространять отношение подкласса.
Первое полезно, когда вы хотите сделать что-то общее, но вам нужно полагаться на определенный набор методов в B
. Например, если у вас есть класс Output
с методом toFile
, вы можете использовать этот метод в любом классе, который может быть передан в Q
.
Второй полезен, когда вы хотите создавать коллекции, которые ведут себя так же, как и исходные классы. Если вы берете B
и вы создаете подкласс A
, вы можете передать A
в любом месте, где ожидается B
. Но если вы берете коллекцию B
, Q[B]
, это правда, что вы всегда можете перейти в Q[A]
вместо этого? В общем, нет; бывают случаи, когда это было бы неправильно. Но вы можете сказать, что это правильно, если использовать +B
(ковариация; Q
covaries - следует вместе с отношением наследования подклассов B
).
Я хотел бы расширить отличный ответ Рекса Керра с еще несколькими примерами: Пусть говорят, что у нас есть четыре класса:
class Animal {}
class Dog extends Animal {}
class Car {}
class SportsCar extends Car {}
case class List[+B](elements: B*) {} // simplification; covariance like in original List
val animals: List[Animal] = List( new Dog(), new Animal() )
val cars: List[Car] = List ( new Car(), new SportsCar() )
Как вы видите, List не заботится о том, содержит ли он Животные или Автомобили. Разработчики списка не применяли это, например. Только автомобили могут войти в списки.
Дополнительно:
case class Shelter(animals: List[Animal]) {}
val animalShelter: Shelter = Shelter( List(new Animal()): List[Animal] )
val dogShelter: Shelter = Shelter( List(new Dog()): List[Dog] )
Если функция ожидает параметр List[Animal]
, вы также можете передать List[Dog]
в качестве аргумента функции. List[Dog]
считается подклассом List[Animal]
из-за ковариации List. Это не сработает, если List будет инвариантным.
case class Barn[A <: Animal](animals: A*) {}
val animalBarn: Barn[Animal] = Barn( new Dog(), new Animal() )
val carBarn = Barn( new SportsCar() )
/*
error: inferred type arguments [SportsCar] do not conform to method apply type parameter bounds [A <: Animal]
val carBarn = Barn(new SportsCar())
^
*/
Как вы можете видеть Barn - это коллекция, предназначенная только для животных. Здесь нет автомобилей.
для моего понимания:
Первый - это связанный с параметром тип, в нашем случае верхний и нижний типы - это "параметр типа A, который является подтипом B (или самого B).
Вторая - это аннотация вариации для определения класса, в нашем случае ковариационная подклассов B
Scala: + Java:? расширяет T ковариационное подклассов
Scala: - Java:? супер T контравариантная подклассов
Я нашел это сообщение в блоге, исследуя этот вопрос. Дает еще более глубокое объяснение дисперсии Scala, включая ее теоретическую основу в Теории категорий
http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/