What's the difference between A<:B and +B in Scala?

12,380

Solution 1

Q[A <: B] means that class Q can take any class A that is a subclass of B.

Q[+B] means that Q can take any class, but if A is a subclass of B, then Q[A] is considered to be a subclass of Q[B].

Q[+A <: B] means that class Q can only take subclasses of B as well as propagating the subclass relationship.

The first is useful when you want to do something generic, but you need to rely upon a certain set of methods in B. For example, if you have an Output class with a toFile method, you could use that method in any class that could be passed into Q.

The second is useful when you want to make collections that behave the same way as the original classes. If you take B and you make a subclass A, then you can pass A in anywhere where B is expected. But if you take a collection of B, Q[B], is it true that you can always pass in Q[A] instead? In general, no; there are cases when this would be the wrong thing to do. But you can say that this is the right thing to do by using +B (covariance; Q covaries--follows along with--B's subclasses' inheritance relationship).

Solution 2

I would like to extend Rex Kerr's excellent answer with some more examples: Let's say we have four classes:

 class Animal {}
 class Dog extends Animal {}

 class Car {}
 class SportsCar extends Car {}

Let's start with variance:

 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() )

As you can see List does not care whether it contains Animals or Cars. The developers of List did not enforce that e.g. only Cars can go inside Lists.

Additionally:

case class Shelter(animals: List[Animal]) {}

val animalShelter: Shelter = Shelter( List(new Animal()): List[Animal] )
val dogShelter: Shelter = Shelter( List(new Dog()): List[Dog] )

If a function expects a List[Animal] parameter you can also pass a List[Dog] as an argument to the function instead. List[Dog] is considered a subclass of List[Animal] due to the covariance of List. It would not work if List was invariant.

Now onto type bounds:

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())
                 ^
*/

As you can see Barn is a collection only intended for Animals. No cars allowed in here.

Solution 3

I found this blog post while researching this question. Gives an even deeper explanation of Scala variance including its theoretical basis in Category Theory

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

Solution 4

for my Understanding:


The first is a parameter type bound, there a upper and lower typebounds in our case its a "type parameter A that is a subtype of B (or B itself).


The second is a Variance Annotation for a class defintion, in our case a covariance subclassing of B


Scala: + Java: ? extends T Covariant subclassing

Scala: - Java: ? super T Contravariant subclassing

Share:
12,380
jsadfeew
Author by

jsadfeew

Updated on June 26, 2022

Comments

  • jsadfeew
    jsadfeew almost 2 years

    What's the difference between

    [A <: B]
    

    and

    [+B]
    

    in Scala?

  • Namek
    Namek over 9 years
    Animal-Dog / Car-SportsCar classes are always best examples. It's pretty hard to follow all those Q, A, Bs.
  • moshe beeri
    moshe beeri over 9 years
    Can you give an example and explanation for the use of contravariant? What Q[-A] means ?
  • Zxcv Mnb
    Zxcv Mnb about 7 years
    In List[Car] we can of course add SportsCar, even if it was declared as List[B]. If we had function like def smth(x:MyList[Car]):Unit = {}, passing List[SportsCar] would fail for List[B], but not for List[+B], because now List[SportsCar] is subclass of List[Car], and we can pass sub classes in place of original class.