Chapter 11. Variance Behavior and Equality

An important concept in object-oriented type systems goes by the name variance under inheritance. More specifically, we need well-defined rules for when an instance of one type can be substituted for an instance of another type. This chapter begins with an exploration of these concepts.

A logical follow-on is the subject of instance equality, which is trickier than it might seem in object-oriented languages.

Parameterized Types: Variance Under Inheritance

Suppose a val is declared of type Seq[AnyRef]. Are you allowed to assign a Seq[String] to it? In other words, is Seq[String] considered substitutable for Seq[AnyRef]? The Liskov substitution principle (LSP) was the first to define formally what this means. In OOP, LSP is defined using type hierarchies. Instances of one type A are substitutable for instances of another type B if A is a subtype of B. Since AnyRef is a supertype of all reference types, like String, instances of String are substitutable where instances of AnyRef are required. (We’ll discuss the Scala type hierarchy in depth in Chapter 13.)

So what about parameterized types, such as collections like Seq[AnyRef] and Seq[String]? Let’s look at immutable parameterized types first.

The type parameter A is declared like this, Seq[+A], where +A means that Seq is covariant in the A parameter. Since String is substitutable for AnyRef, then Seq[String] is substitutable for a Seq[AnyRef]. Covariance means the supertype-subtype relationship ...

Get Programming Scala, 3rd Edition now with the O’Reilly learning platform.

O’Reilly members experience live online training, plus books, videos, and digital content from nearly 200 publishers.