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 ...
Become an O’Reilly member and get unlimited access to this title plus top books and audiobooks from O’Reilly and nearly 200 top publishers, thousands of courses curated by job role, 150+ live events each month,
and much more.
Read now
Unlock full access