T <: A
declares that type variable T
refers to a subtype of type A
(or type A
itself).
T >: A
expresses that the type parameter T
or the abstract type T
refers to a supertype of type A
(or type A
itself).
In most cases, A
will be the type parameter of the class and B
will be the type parameter of a method.
def foo[T >: A with B <: C]()
says that T
must be a super type of (or equal to) A with B
and a subtype of (or equal to) C
.
Detailed example
Here's a contrived, but hopefully more helpful example showing what works and doesn't work with type bounds like this:
trait Color
trait Warm extends Color
trait Red extends Warm
trait Neutral extends Color
trait Expensive
trait Gold extends Warm with Expensive
type WarmAndExpensiveSuper >: Warm with Expensive
type WarmAndExpensiveSuperColor = WarmAndExpensiveSuper with Color
type Exact >: Warm with Expensive <: Color
def foo[T >: Warm with Expensive <: Color]: T
// `>: Warm with Expensive` means `T` must be a super type of `Warm` or `Expensive` or both, or itself be of type `Warm with Expensive`
// `<: Color` means `T` must be a sub type of `Color`, or itself be of type `Color`
foo[Color] // ✅ Works because `Color` is a super type of `Warm` and `Color` is `Color`
foo[Warm] // ✅ Works because `Warm` is itself `Warm` and `Warm` is a sub type of `Color`
foo[Red] // ⛔️ Doesn't work because `Red` is neither a super type of `Warm` nor `Expensive`
foo[Neutral] // ⛔️ Doesn't work because `Neutral` is neither a super type of `Warm` nor `Expensive`
foo[Gold] // ⛔️ Doesn't work because `Gold` is neither a super type of `Warm` nor `Expensive` (it's a sub type of both)
foo[Expensive] // ⛔️ Doesn't work because `Expensive` is not a sub type of `Color`
foo[WarmAndExpensiveSuper] // ⛔️ Doesn't work because `WarmAndExpensiveSuper` is not a sub type of `Color` (it's some super type of either `Warm` or `Expensive` or both)
foo[WarmAndExpensiveSuperColor] // ✅ Works because it is `WarmAndExpensiveSuper`, which is a super type of both `Warm` and `Expensive`, and it is a sub type of `Color`
foo[Exact] // ✅ Works because `Exact` matches the type bounds exactly
Unlike type bounds, a context bound of T: A
is just syntactic sugar for an implicit
parameter of type A[T]
.
This is typically used with the type class pattern.
def foo[T: A](t : T)
is syntactic sugar for
def foo[T](t : T)(implicit val a: A[T])
A =:= B
means A
must be exactly B
A <:< B
means A
must be a subtype of B
(analogous to the simple type constraint <:
)
A <%< B
(deprecated) means A
must be viewable as B
, possibly via implicit conversion (analogous to the deprecated view (type) bound <%
)
case class Foo[A](a: A) {
// This method can only be used if this is a Foo[String]
def getStringLength(implicit evidence: A =:= String) = a.length
}
- https://docs.scala-lang.org/tour/upper-type-bounds.html
- https://docs.scala-lang.org/tour/lower-type-bounds.html
- https://stackoverflow.com/questions/4465948/what-are-scala-context-and-view-bounds/4467012#4467012
- https://harmeetsingh.gitbook.io/scala-type-system/phase-i/chapter-9-type-constraints
- https://docs.scala-lang.org/tutorials/FAQ/index.html