Variance
Scala's type system includes variance annotations that influence how subtyping between more complex types works, such as between generic classes of those types.
- Covariance (
+T): IfAis a subtype ofB, thenBox[A]is a subtype ofBox[B]. - Contravariance (
-T): IfAis a subtype ofB, thenBox[B]is a subtype ofBox[A]. - Invariance: By default, generic types in Scala are invariant. If
Ais a subtype ofB, there is no relationship betweenBox[A]andBox[B].
Example of covariance:
class Container[+A]
val animalContainer: Container[Animal] = new Container[Cat] // Cat is a subtype of Animal
Bounds
Scala allows you to restrict the types that can be used as type parameters through bounds.
-
Upper Bounds (
<:): Specifies that a type parameter must be a subtype of a particular type.def printName[T <: Animal](animal: T): Unit = { println(animal.name) } -
Lower Bounds (
>:): Specifies that a type parameter must be a supertype of a particular type. -
View Bounds (deprecated in Scala 2.11 and removed in Scala 3): Were used to demand that there exists an implicit conversion from a type
Tto another type. -
Context Bounds (
[T: Ordering]): Useful for requiring an implicit value of a certain type, such as anOrdering[T]for sorting.
Type Constraints
Scala also supports type constraints that allow more control over the relationships between type parameters.
<%(View Bound): Deprecated.<:<(Upper Type Bound): Ensures one type is a subtype of another.=:!=(Not Equal): Ensures two types are not the same.
def foo[A, B](a: A, b: B)(implicit ev: A <:< B): B = b