Skip to content
Search

Scala:Variances

lastmod 2020-09-13created 2020-09-116 edits

Scala supports variance annotations for the type parameters of generic classes. In Java 5 (also known as JDK 1.5), clients decide the variance annotation when an abstracted class is used, whereas in Scala, variance annotations can be added when the abstracted class is defined.

Example

The page on generic classes looked at an example of a mutable stack and explained that the type defined by class Stack[T] requires the subtyping of its type parameter to be invariant. This can restrict reuse of the class abstraction. We now look at an implementation of a functional (i.e., immutable) stack without this restriction. Note that this is a more advanced example combining important concepts such as Polymorphic Methods, Lower Type Bounds, and covariant type parameter annotations. It also uses Inner Classes so that stack items can be chained together without explicit links.

class Stack[+T] {
  def push[S >: T](elem: S): Stack[S] = new Stack[S] {
    override def top: S = elem
    override def pop: Stack[S] = Stack.this
    override def toString: String =
      elem.toString + " " + Stack.this.toString
  }
  def top: T = sys.error("no element on stack")
  def pop: Stack[T] = sys.error("no element on stack")
  override def toString: String = ""
}

object VariancesTest extends App {
  var s: Stack[Any] = new Stack().push("hello")
  s = s.push(new Object())
  s = s.push(7)
  println(s)
}

In the stack example, defining method push requires using the covariant type parameter T in a contravariant position. Because the stack's subtyping has to be covariant, we used a technique that abstracts over the parameter type of method push. That is, using T, the type of the stack items, we made a polymorphic method in which T is set as the lower bound of push's type variable. This makes the variance under the declaration of T match that of a covariant type parameter. The stack is now covariant, but with the approach covered so far, operations such as pushing a string onto an integer stack become possible. Such an operation produces a stack of type Stack[Any], so where an integer stack is expected as the result, this result causes an error. Even if no error occurs, all you get is a stack whose item type is more general.

Covariant position

  • The annotation +T declares a type T that can be used only in covariant positions.
  • For a covariant type parameter, the subtyping is covariant according to the definition of the type parameter.
    • That is, if T is a subtype of S, then Stack[T] in this example is a subtype of Stack[S].

Contravariant position

  • -T declares a T that can be used only in contravariant positions.

About

class Foo[+A] // A covariant class
class Bar[-A] // A contravariant class
class Baz[A]  // An invariant class

See also

Favorite site

author Zer0 sitemap