В чем разница между A <: B и + B в Scala? - PullRequest
64 голосов
/ 25 декабря 2010

Какая разница между

[A <: B]

и

[+B]

в Скале?

Ответы [ 4 ]

166 голосов
/ 26 декабря 2010

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 ковариация - следует вместе с - B отношения наследования подклассов).

43 голосов
/ 28 февраля 2014

Я хотел бы расширить Отличный ответ Рекса Керра еще несколькими примерами: Допустим, у нас есть четыре класса:

 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 был инвариантным.

Теперь на границах типов:

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's type parameter bounds [A <: Animal]
    val carBarn = Barn(new SportsCar())
                 ^
*/

Как видите Амбар - это коллекция, предназначенная только для животных . Здесь запрещены автомобили.

2 голосов
/ 20 сентября 2013

Я нашел этот пост в блоге, исследуя этот вопрос. Дает еще более глубокое объяснение дисперсии Scala, включая ее теоретическую основу в теории категорий

http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/

2 голосов
/ 25 декабря 2010

для моего понимания:


Первый - это привязка типа параметра, в нашем случае верхняя и нижняя границы типа - это «тип параметра A, который является подтипом B (или самого B).


Вторым является аннотация дисперсии для определения класса, в нашем случае ковариационное подклассирование B


Scala: + Java:? Расширяет T ковариантное подклассирование

Scala: - Java:? Super T контравариантное подклассы

...