parameter - ¿Cuál es la diferencia entre A<: B y+B en Scala?
scala type (4)
Cuál es la diferencia entre
[A <: B]
y
[+B]
en Scala?
Encontré esta publicación en el blog mientras investigaba esta pregunta. Da una explicación aún más profunda de la varianza de Scala, incluida su base teórica en la teoría de la categoría
http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/
Me gustaría extender la excelente respuesta de Rex Kerr con algunos ejemplos más: digamos que tenemos cuatro clases:
class Animal {}
class Dog extends Animal {}
class Car {}
class SportsCar extends Car {}
Comencemos con la varianza:
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() )
Como puede ver, a List no le importa si contiene Animals o Cars . Los desarrolladores de List no hicieron cumplir que, por ejemplo, solo los automóviles pueden entrar dentro de listas.
Adicionalmente:
case class Shelter(animals: List[Animal]) {}
val animalShelter: Shelter = Shelter( List(new Animal()): List[Animal] )
val dogShelter: Shelter = Shelter( List(new Dog()): List[Dog] )
Si una función espera un parámetro List[Animal]
también puede pasar una List[Dog]
como argumento a la función. List[Dog]
se considera una subclase de List[Animal]
debido a la covarianza de List. No funcionaría si List fuera invariante.
Ahora en los límites de tipo:
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())
^
*/
Como puedes ver, Barn es una colección solo para Animales . No se permiten autos aquí.
Para mi entendimiento:
El primero es un tipo de parámetro vinculado, hay un tipo de letra superior e inferior en nuestro caso es un "tipo de parámetro A que es un subtipo de B (o B mismo).
El segundo es una anotación de varianza para una definición de clase, en nuestro caso una subclasificación de covarianza de B
Scala: + Java:? extiende la subclasificación T covariante
Scala: - Java:? superc T Subclase contravariante
Q[A <: B]
significa que la clase Q
puede tomar cualquier clase A
que sea una subclase de B
Q[+B]
significa que Q
puede tomar cualquier clase, pero si A
es una subclase de B
, entonces Q[A]
se considera una subclase de Q[B]
.
Q[+A <: B]
significa que la clase Q
solo puede tomar subclases de B
y propagar la relación de subclase.
El primero es útil cuando quiere hacer algo genérico, pero necesita basarse en cierto conjunto de métodos en B
Por ejemplo, si tiene una clase de Output
con un método toFile
, puede usar ese método en cualquier clase que se pueda pasar a Q
El segundo es útil cuando quiere hacer colecciones que se comporten de la misma manera que las clases originales. Si tomas B
y haces una subclase A
, entonces puedes pasar A
en cualquier lugar donde se espera B
Pero si tomas una colección de B
, Q[B]
, ¿es verdad que siempre puedes pasar Q[A]
? En general, no; hay casos en que esto sería lo incorrecto. Pero puede decir que esto es lo correcto al usar +B
(covarianza; Q
covarios - sigue junto con-- la relación de herencia de las subclases de B
).