Kotlin: An Illustrated Guide • Chapter 19

# Generic Variance

As we learned in Chapter 12, we can substitute a subtype anywhere that a supertype is declared.

When it comes to generics, though, things don’t always work the way we might expect. While `MutableList<Cow>` is a subtype of `List<Cow>`, it’s not a subtype of `MutableList<FarmAnimal>`.

With a little bit of magic, though, we can finagle parameterized types into becoming the subtypes that we need them to be. To do that, we first need to learn about covariance and contravariance, so let’s jump in!

## Covariance

Parker works for the local Parks & Recreation Department. Since the playground is often full of parents with hungry children, he thought, “Let’s add a vending machine in the pavilion.” So he spoke with Vinnie, a guy who owns a nearby business that sets up and stocks vending machines.

“I need a vending machine where kids can put in a coin and get back a snack”, said Parker. Vinnie agreed, so they drew up a contract.

Once the paperwork was signed, Vinnie installed the new vending machine at the park, where customers could insert a coin and receive a random snack, like trail mix, a bag of gummy bears, or a candy bar. The next day, when Parker saw a kid insert a nickel and receive a bag of trail mix from the machine, he smiled to himself, knowing all was well.

A few weeks later, Parker saw Vinnie replacing the machine. Parker was a bit nervous about the change, so he spoke to him. “This new machine is still going to accept a coin and return a snack, right?”

“Yes”, began Vinnie, “Trail mix and gummy bears are in short supply, though, so this vending machine is only going to return a candy bar. That’s still okay, right?”

Parker thought about it, and finally replied, “As long as it accepts a coin and returns a snack. A candy bar is a kind of snack, so that should be fine.”

After the new machine was substituted for the old one, a child came up, inserted a coin, and received a candy bar. Parker smiled, glad that the new machine did what it was supposed to.

A few weeks passed. One day, Vinnie was at it again, replacing the machine with yet another one. A few minutes after the installation was complete, an irate father complained to Parker, “What’s wrong with the new vending machine? My hungry kid put in a coin and instead of getting a snack, he got a toy action figure - he can’t eat a toy!

Parker quickly caught up with Vinnie again. When asked about the new machine, Vinnie replied, “Oh, I thought I’d switch out the old vending machine with a new one that returns either a snack or a toy.”

“No, no, no! The contract said the machine would accept a coin and return a snack. Toys are not snacks!” Realizing that this new machine didn’t do what Parker needed, Vinnie agreed to resolve the issue.

### Covariance and Substitution

As we can see from this story, some substitutions work, but others don’t.

• When Vinnie replaced the original vending machine with one that only returns candy bars, everything was fine, because candy bars are still a kind of snack that kids can eat.
• However, when he replaced it with the machine that returns either a snack or a toy, he broke his contract - the machine no longer returned only snacks.

This brings up some important points for us to explore about substitution in Kotlin. To get started, let’s model the first vending machine that Vinnie provided - the one that accepted a coin and returned a snack.

``````open class VendingMachine {
open fun purchase(money: Coin): Snack = randomSnack()
}``````

Although this chapter won’t include definitions for `Snack` and its related types, here’s a class diagram that shows them with their type hierarchies.

Now let’s model Vinnie’s second vending machine by extending the one above. This one looks almost identical to it, but instead of a random snack, it only ever returns a candy bar.

``````class CandyBarMachine : VendingMachine() {
override fun purchase(money: Coin): Snack = CandyBar()
}``````

Any code that expects a `VendingMachine` can work with a `CandyBarMachine`, because it’s a subtype of `VendingMachine` - it still accepts a coin and returns a snack, just like `VendingMachine` does. Because of this, we can substitute a `CandyBarMachine` for a `VendingMachine`. In other words, we can assign a `CandyBarMachine` to a variable declared as a `VendingMachine`.

Similarly, we can send it as an argument to a function that excepts a `VendingMachine`.

As we saw in the story, a `CandyBar` is a kind of `Snack`, so it’s also safe to declare that the `CandyBarMachine` only returns `CandyBar` objects. Let’s take the code from Listing 19.2 and update the return type.

``````class CandyBarMachine : VendingMachine() {
override fun purchase(money: Coin): CandyBar = CandyBar()
}``````

As the story revealed, this only works one way. When Vinnie tried to replace the vending machine with one that returned a product that could be either a snack or a toy, he broke his contract. Kids expect to be able to eat anything that the vending machine provides, but toys aren’t edible!

The same principle holds true in Kotlin - a subclass can’t return a more general type. For example, if we create a `ToyOrSnackMachine` that tries to return any kind of `Product`, we’ll get a compile-time error.

``````class ToyOrSnackMachine : VendingMachine() {
override fun purchase(money: Coin): Product = randomToyOrSnack()
}``````
Error

Most classes and interfaces associate with other types. Associated types can appear in a variety of places - as function parameter types, function return types, property types, and so on. For example, the `VendingMachine` class associates with two other types:

• `Coin` - the type of the parameter in `purchase()`
• `Snack` - the return type of `purchase()`

The `VendingMachine` class can have supertypes and subtypes. Similarly, the associated types `Coin` and `Snack` can also have their own supertypes and subtypes.

The nature of the relationship between the type hierarchy of `VendingMachine` and the type hierarchy of its associated types can be described by variance. As we look at the code of `VendingMachine` and `CandyBarMachine` side by side, we can see that relationship emerge.

Specifically, we can see that as we make a more specific `VendingMachine`, we can return a more specific `Snack` from the `purchase()` function. Because they become more specific together, this kind of variance is called covariance, where “co-” is a prefix that means “together”. When talking about variance, developers normally say it one of these ways:

• “A type is covariant with regard to its return types”
• “A type is covariant on its return types”

To sum up this section, just remember that within a subtype (e.g., `CandyBarMachine`) a function can return a more specific type (e.g., `CandyBar`) than declared in its supertype, but not a more general type.

The adventures of Parker and Vinnie continue, though! Brace yourself - Vinnie is about to try substituting a few more vending machines!

## Contravariance

A few weeks later, Parker saw Vinnie replacing the machine yet again. “We’re upgrading our machines so that they accept both coins and bills,” he explained.

Parker thought about it. “Well, I guess that’s fine. As long as the machine still accepts a coin and returns a snack.” After the new machine was installed, a kid walked up, inserted a dime, and got a snack from the machine. “Great,” Parker thought to himself, “Everything is still good”.

Things continued well over the next few weeks. Even though this new vending machine had a bill acceptor, none of the kids ever used it, because they only ever had coins.

Wouldn’t you know it - a few weeks later, Parker saw Vinnie replacing the machine one more time. Parker shrugged it off and continued on his way.

Ten minutes later, though, a young girl was crying next to the snack machine. “What’s wrong?” he asked her.

“I wanted a snack. I have a nickel, but this machine only accepts a dime!” Parker looked at the new vending machine, and sure enough, it only had a slot for dimes, but not any other kind of coin.

Parker complained to Vinnie again. “No, no, no! The contract said the machine would accept a coin and return a snack. A nickel is a kind of coin, so the machine still needs to accept it!” Embarrassed, Vinnie put back the previous vending machine.

### Contravariance and Substitution

This story shows again that some substitutions work, and some don’t.

• When Vinnie replaced the original vending machine with the one that accepted both coins and bills, everything still worked, because it still accepted coins. The kids only ever had coins on them, so they never actually used the bill acceptor, but there was no harm in the machine having one, as long as it could still accept coins.
• However, when Vinnie replaced it with the machine that accepted only dimes, he broke his contract - the machine no longer accepted nickels, quarters, or any other kind of coin.

This illustrates additional points about substitution. Let’s take a look at the original `VendingMachine` we created back in Listing 19.1.

``````open class VendingMachine {
open fun purchase(money: Coin): Snack = randomSnack()
}``````

As with `Snack`, this chapter won’t include any class definitions for `Coin` and its type hierarchy, but here’s a UML class diagram showing how they relate.

As we saw, it was safe for Vinnie to replace the coin-based vending machine with one that can accept either coins or bills. Likewise, it would be safe for Kotlin to allow a subtype to declare a more general parameter type.

But guess what? If we try changing the parameter type from `Coin` to `Money`, we’ll get a compiler error!

``````class AnyMoneyVendingMachine : VendingMachine() {
override fun purchase(money: Money): Snack = randomSnack()
}``````
Error

Again, this would be perfectly safe for Kotlin’s type system to allow. So why does this cause a compiler error?

As you might recall, Kotlin allows us to overload a function - a class or interface can have multiple functions that have the same name, as long as their parameter types differ. In order to support this feature, Kotlin won’t allow us to change the type of a function parameter in subtypes, as we’re trying to do above.

One way to fix this is to simply remove the `override` keyword, which means we’ll be overloading the function instead of overriding it.

``````class AnyMoneyVendingMachine : VendingMachine() {
fun purchase(money: Money): Snack = randomSnack()
}``````

Just keep in mind that when we do this, we end up with two `purchase()` functions, each with its own body - one in `AnyMoneyVendingMachine` and one in `VendingMachine`.

So Kotlin’s overloading feature is getting in the way. Overloading only applies to functions declared with the `fun` keyword, though, so to work around this, we can change `purchase()` from a function to a property that has a function type, like this.

``````open class VendingMachine {
open val purchase: (Coin) -> Snack = { randomSnack() }
}``````

With this change, we can rewrite `AnyMoneyVendingMachine` to override that property.

``````class AnyMoneyVendingMachine : VendingMachine() {
override val purchase: (Coin) -> Snack = { randomSnack() }
}``````

And finally, we can replace the `Coin` parameter type with `Money`.

``````class AnyMoneyVendingMachine : VendingMachine() {
override val purchase: (Money) -> Snack = { randomSnack() }
}``````

With this, we can assign an instance of `AnyMoneyVendingMachine` to a variable whose type is `VendingMachine`. When assigned to a `VendingMachine` variable, it can only accept a coin. But when assigned to an `AnyMoneyVendingMachine`, it can accept either a coin or a bill.

``````val vendingMachine: VendingMachine = AnyMoneyVendingMachine()
val anyMoneyMachine: AnyMoneyVendingMachine = AnyMoneyVendingMachine()

val snack1: Snack = vendingMachine.purchase(Dime())
val snack2: Snack = anyMoneyMachine.purchase(Dime())
val snack3: Snack = anyMoneyMachine.purchase(OneDollarBill())``````

As we did earlier, we can put these two classes side by side and discover the nature of the variance between the class type and the return type of `purchase()`.

This time, we can see that as we make a more specific `VendingMachine`, we can accept a more general parameter type in `purchase`. The arrows above are pointing in opposite directions, so this kind of variance is called contravariance.

As you recall from the story, when Vinnie tried to replace the vending machine with one that accepted a more specific kind of coin, he broke his contract. Similarly, we can’t create a subclass that accepts a more specific type than its superclass did. For example, if we update `purchase` so that its parameter type is `Dime`, we’ll get a compiler error.

``````class AnyMoneyVendingMachine : VendingMachine() {
override val purchase: (Dime) -> Snack = { randomSnack() }
}``````
Error

So, we learned from this story that a subtype can declare that it accepts a more general type, but not a more specific type.

Now that we understand variance - including covariance and contravariance - its time to revisit what we learned, and see how these concepts apply to generics.

## What Makes a Subtype a Subtype?

Whenever Vinnie replaced one vending machine with another, everything was fine as long as the new machine did everything that the contract said it would. Specifically, the contract stated that the vending machine must accept a coin and return a snack. If the new machine did those things, then it was a suitable substitute. On the two occasions when it broke the contract, it was not a suitable substitute.

So what makes a subtype a subtype? The ability to substitute it for one of its supertypes. A subtype must fully support the contract of its supertype. Specifically, this means it must obey the following three rules:1

1. The subtype must have all of the same public properties and functions as its supertype.
2. Its function parameter types must be the same as or more general than the ones in its supertype.
3. Its function return types must be the same as or more specific than the ones in its supertype.

We usually think of a subtype as a class that extends another class, an interface that extends another interface, or a class that implements an interface. In all three of these cases, Kotlin’s type system will ensure that the subclass follows the three rules above. However, when it comes to parameterized types - such as `VendingMachine<Snack>` and `VendingMachine<CandyBar>` - we can’t explicitly declare that one type is a subtype of another.

To help demonstrate this, let’s convert `VendingMachine` to a generic class. As a part of this change, we’ll add a `snack` constructor parameter, which is the snack that will get returned when the `purchase()` function is called.2

``````class VendingMachine<T : Snack>(private val snack: T) {
fun purchase(money: Coin): T = snack
}``````

As we learned in the last chapter we can create parameterized types from this generic, such as `VendingMachine<Snack>` and `VendingMachine<CandyBar>`. Even though we can’t explicitly declare that `VendingMachine<CandyBar>` is a subtype of `VendingMachine<Snack>`, it would be perfectly safe for this to be the case, because it wouldn’t break the contract - all three rules above would be satisfied.

We can visualize this by imagining what the effective parameterized type would look like. In other words, let’s take the generic `VendingMachine` above, and everywhere that the type parameter appears, we’ll replace it with the type argument. Are all three rules satisfied?

So, `VendingMachine<CandyBar>` fully satisfies the contract of `VendingMachine<Snack>`, which means it’s safe for it to be one of its subtypes. This won’t just happen automatically, though. For example, if we try assigning it to a variable declared as `VendingMachine<Snack>`, we’ll get a compiler error.

``````val candyBarMachine: VendingMachine<CandyBar> = VendingMachine(CandyBar())
val vendingMachine: VendingMachine<Snack> = candyBarMachine``````
Error

So, `VendingMachine<CandyBar>` won’t be a subtype of `VendingMachine<Snack>` until we tell Kotlin our intent by doing one more thing.

## Variance Modifiers

As we saw above, `VendingMachine<CandyBar>` fully satisfies the contract of `VendingMachine<Snack>`, so it should be possible for it to be a subtype of it. However, a type parameter can potentially be used in lots of places throughout the body of a generic type. It could be used as the return type of a function, as the parameter of a function, as the type of a property, and so on. Let’s consider what would happen if we were to add a `refund()` function to the `VendingMachine` interface.

``````class VendingMachine<T : Snack>(private val snack: T) {
fun purchase(money: Coin): T = snack
fun refund(snack: T): Coin = Dime()
}``````

In this case, the type parameter `T` appears both as a function result type and as a function parameter type. Would this version of `VendingMachine<CandyBar>` satisfy the contract of `VendingMachine<Snack>`? Let’s compare the effective parameterized types again. First, let’s look at the `purchase()` function.

As before, when it comes to the `purchase()` function, `VendingMachine<CandyBar>` satisfies the contract of `VendingMachine<Snack>`. So far so good. Now let’s look at the `refund()` function.

Yikes! Even though the return type of `refund()` satisfies the contract, the parameter type does not, because `CandyBar` is not the “same as or more general than” `Snack` - it’s more specific. So, if `VendingMachine<T>` includes the `refund()` function, then `VendingMachine<CandyBar>` cannot be a subtype of `VendingMachine<Snack>`.

If Kotlin is going to treat `VendingMachine<CandyBar>` as a subtype of `VendingMachine<Snack>`, we have to promise Kotlin that we won’t use this type parameter as a function parameter type, like we did in `refund()`. Instead, it can only appear in out-positions - in other words, as a function return type, or as the type of a read-only property.

To make this promise, we can add a variance modifier to the type parameter. Kotlin has two variance modifiers - let’s check them out.

### The `out` modifier

The first variance modifier is named `out`, and it’s our way of telling Kotlin that this type parameter will only appear in an out-position. Let’s add the `out` modifier to type parameter `T`.

``````class VendingMachine<out T : Snack>(private val snack: T) {
fun purchase(money: Coin): T = snack
}``````

This simple change is all that’s needed to get the code from Listing 19.14 to compile without errors - `VendingMachine<CandyBar>` is now a subtype of `VendingMachine<Snack>`!

``````val candyBarMachine: VendingMachine<CandyBar> = VendingMachine(CandyBar())
val vendingMachine: VendingMachine<Snack> = candyBarMachine``````

As we saw earlier in this chapter, a type is covariant with its associated function result types. So by ensuring that this type parameter is only used as a result type, we know that it’s safe for the `VendingMachine` type to be covariant with `T`.

Again, the `out` modifier is a promise to Kotlin that we will only use the type parameter in the out-position. If we try to use this same type parameter in an in-position - that is, as a function parameter type - then we’ll get a compiler error. To demonstrate this, we can pop in the `refund()` function from Listing 19.15, and watch as the compiler calls us out for breaking our promise.

``````class VendingMachine<out T : Snack>(private val snack: T) {
fun purchase(money: Coin): T = snack
fun refund(snack: T): Coin = Dime()
}``````
Error

So, by adding the `out` modifier to the type parameter, we’ve got the advantage that `VendingMachine<CandyBar>` is now a subtype of `VendingMachine<Snack>`, but we’ve got the disadvantage that we can no longer use `T` in an in-position.

### The `in` modifier

As you probably guessed, Kotlin includes a complement to the `out` modifier, called `in`. But before we look at it, let’s shake things up in the `VendingMachine` class. Instead of a type parameter for its snack type, let’s use the type parameter for its money type.

``````class VendingMachine<T : Money> {
fun purchase(money: T): Snack = randomSnack()
}``````

Similar to before, we can try to assign an instance of `VendingMachine<Money>` to `VendingMachine<Coin>`, but we’ll get an error.

``````val moneyVendingMachine: VendingMachine<Money> = VendingMachine()
val coinVendingMachine: VendingMachine<Coin> = moneyVendingMachine``````
Error

Even though `T` is only being used in the in-position, we have to declare this to Kotlin with the `in` variance modifier.

``````class VendingMachine<in T : Money> {
fun purchase(money: T): Snack = randomSnack()
}``````

With this change, the code from Listing 19.20 successfully compiles.

``````val moneyVendingMachine: VendingMachine<Money> = VendingMachine()
val coinVendingMachine: VendingMachine<Coin> = moneyVendingMachine``````

As with the `out` modifier, we’ve made a trade-off here: when we declare a type parameter with the `in` modifier, we’re promising Kotlin that it will only ever appear in the in-position. Again, if we add a `refund()` function, we would need `T` in the out-position - as that function’s return type - which would cause a compiler error.

``````class VendingMachine<in T : Money>(private val money: T) {
fun purchase(money: T): Snack = randomSnack()
fun refund(snack: Snack): T = money
}``````
Error

In summary:

• The `out` modifier can be used to ensure that the type parameter will only appear publicly in an out-position, which makes it safe for covariance.
• Conversely, the `in` modifier can be used to ensure that it will only appear publicly in an in-position, so that it’s safe for contravariance.

In order to keep things simple, we’ve only used one type parameter at a time so far in this chapter. It’s entirely possible to have multiple type parameters, though, and when we do, we can use a variance modifier on each one. Let’s see how that looks.

## Variance on Multiple Type Parameters

It’s important to note that generic variance doesn’t describe the type as a whole - it describes the relationship of the type to one of its type parameters. So, it can be covariant on one and contravariant on another. Instead of a single type parameter for either the `Money` or `Product`, let’s include one for each.

``````class VendingMachine<in T : Money, out R: Product>(private val product: R) {
fun purchase(money: T): R = product
}``````

In this code, `VendingMachine` is contravariant with respect to `T`, and covariant with respect to `R`. From this generic class, we could instantiate a wide range of parameterized types. Some of them would include:

With two type parameters, all those types of money, and all those types of product, it’s easy to get lost in which of the parameterized types are subtypes of the others. Just remember that a subtype must fully support the contract of its supertype. Here’s how the type hierarchy looks for the parameterized types above.

It’s helpful to know that we can apply variance modifiers to multiple type parameters. For the rest of this chapter, though, we’ll go back to a single type parameter in order to keep the examples easy to follow.

We’ve seen how the `in` and `out` modifiers create variance, but we’ve also seen the trade-offs - type parameters declared with `in` can’t be used as a result type, and those declared with `out` can’t be used as a function parameter type. But sometimes, those trade-offs just won’t cut it. When that’s the case, we can still get some of the benefits of variance by using type projections.

## Type Projections

So far, we’ve been able to get `VendingMachine<CandyBar>` to be a subtype of `VendingMachine<Snack>` by using variance modifiers on our type parameters. Let’s update the code from Listing 19.16 so that it works with any kind of `Product` and any kind of `Money`.

``````class VendingMachine<out T : Product>(private val product: T) {
fun purchase(money: Money): T = product
}``````

In this code, we’ve got `out` on the type parameter again. Because we put this variance modifier where the type parameter is declared, this is called declaration-site variance. We can’t always use declaration-site variance, though. For example, what if we really, truly need that `refund()` function from Listing 19.18?

``````class VendingMachine<T : Product>(private val product: T) {
fun purchase(money: Money): T = product
fun refund(product: T): Money = Dime()
}``````

We can’t use the `out` variance modifier, because `T` is used in an in-position in `refund()`. And we can’t use the `in` modifier, because it’s used in an out-position in `purchase()`. Are we just out of luck, here? Does this mean that `VendingMachine<CandyBar>` will never be considered a subtype of `VendingMachine<Snack>`?

Thankfully, Kotlin provides a second option. Instead of using a variance modifier on a type parameter, we can use it on a type argument. To demonstrate this, let’s start with a function that accepts a `VendingMachine<Snack>`.

``````fun getSnackFrom(machine: VendingMachine<Snack>): Snack {
return machine.purchase(Dime())
}``````

Since there are no variance modifiers on the type parameter in Listing 19.26, `VendingMachine<CandyBar>` is not a subtype of `VendingMachine<Snack>`, so we won’t be able to call the function with an instance of `CandyBarMachine`.

``````val candyBarMachine: VendingMachine<CandyBar> = VendingMachine(CandyBar())
getSnackFrom(candyBarMachine)``````
Error

Now let’s add an `out` modifier to the type argument in the `getSnackFrom()` function, like this.

``````fun getSnackFrom(machine: VendingMachine<out Snack>): Snack {
return machine.purchase(Dime())
}``````

With this change, the code from Listing 19.28 compiles successfully!

``````val candyBarMachine: VendingMachine<CandyBar> = VendingMachine(CandyBar())
getSnackFrom(candyBarMachine)``````

When we put a variance modifier on a type argument rather than a type parameter, we create variance at the place in our code where we’re using the type (e.g., `getSnackFrom()`) instead of where we declared it (e.g., `VendingMachine`). For this reason, we call this use-site variance.

Just like declaration-site variance, use-site variance comes with trade-offs. As we’ll see in a moment, because the `machine` parameter has the `out` modifier, we won’t be able to call the `refund()` function inside the body of this function. Thankfully, the body of this function has no need for the `refund()` function, so this trade-off is entirely acceptable here!

Keep in mind that declaration-site variance applies to the whole project, but use-site variance works only in a specific part of the project where we put the variance modifier on the type argument. In the example code above, it only works for the `getSnackFrom()` function. So, if other functions in our project still need to use the `refund()` function, that’s completely fine. In those places, `VendingMachine<CandyBar>` simply wouldn’t be able to be a subtype of `VendingMachine<Snack>`.

### Out-Projections

It’s important to note that, within the body of this function, `machine` does not have the type `VendingMachine<Snack>`. It has the type `VendingMachine<out Snack>`, which is a type projection. Since this type argument has the `out` modifier on it, this particular kind of type projection is called an out-projection.

What exactly is a projection?

Think of a projection like the shadow of a ball. If you shine a light on it, it casts a shadow onto the wall. The ball itself is a sphere that has three dimensions, but the ball’s shadow on the wall is a circle that has only two dimensions. The shadow still resembles the ball, but it’s missing its depth.3

Similarly, when we create a type projection, it’s kind of like we’re removing some of the “depth” of that object by limiting the types of its function inputs and outputs. For example, in the `getSnackFrom()` function above, we created a type projection from `VendingMachine<Snack>`, called `VendingMachine<out Snack>`, which looks a lot like the original, except that the `refund()` function no longer accepts a `Snack`. Instead, it accepts a type called `Nothing`!

What exactly is `Nothing`? As mentioned in Chapter 14, every type in Kotlin is a subtype of a class named `Any`. Similarly, every type in Kotlin is also a supertype of a class called `Nothing`.

The `Nothing` class can never be instantiated, because it only has a `private` constructor. Since it’s impossible to create an instance of `Nothing`, we’ll never be able to call `refund()` here in this function.

So, an out-projection is created by adding the `out` modifier to a type argument. It looks similar to the original parameterized type, but everywhere that the type argument appears in an in-position, it is replaced with the `Nothing` type.

### In-Projections

As you can probably guess, we can also use an `in` modifier to create an in-projection, like in this function.

``````fun getRefundFrom(machine: VendingMachine<in CandyBar>): Coin {
return machine.refund(CandyBar())
}``````

As with the out-projection above, inside the body of this function, we end up with a projection of `VendingMachine<CandyBar>`. But this time, instead of affecting the functions’ parameter types, it’s the functions’ result types that have been affected.

With an in-projection, everywhere that the type argument appears in an out-position, it is forced to `Any?`. It’s entirely possible to call these functions and get a result. If we want to do anything useful with that result, though, we’ll probably need to cast it back to its more specific type.

So, out-projections and in-projections are two kinds of type projections. Out-projections create covariance by forcing the type argument in in-positions to `Nothing`, and in-projections create contravariance by forcing the type argument in out-positions to `Any?`.

Sometimes we want a function to accept all instances of a generic type, regardless of their type arguments. For these cases, Kotlin includes one more kind of projection.

### Star Projections

Remember Vinnie? Well, at the end of the month, he services all of his vending machines, performing basic maintenance in order to keep them finely-tuned and working well. This operation doesn’t involve any `Money` or `Product` - he just needs to get in there and tighten a few bolts.

Here’s an updated version of `VendingMachine` that includes a function called `tune()`. As you can see, this new function doesn’t use a type parameter at all - neither as a function parameter nor as a return type.

``````class VendingMachine<T : Snack>(private val snack: T) {
fun purchase(money: Coin): T = snack
fun refund(snack: T): Coin = Dime()
fun tune() = println("All tuned up!")
}``````

All vending machines need to be tuned up. The type of the `Snack` that they return is completely irrelevant.

In cases like these, we might want a function to accept literally any kind of type created from `VendingMachine`. Kotlin makes this easy with a special kind of type projection called a star-projection. To create a star-projection, rather than using a variance modifier, simply use an asterisk `*` (that is, a “star”) in place of the type argument. For example, here’s a function that will accept any kind of `VendingMachine`, regardless of its type argument.

``````fun service(machine: VendingMachine<*>) {
print("Tuning up \$machine... ")
machine.tune()
}``````

This function can be called with any kind of `VendingMachine`, regardless of its type argument.

``````service(VendingMachine(CandyBar()) // Works with VendingMachine<CandyBar>
service(VendingMachine(TrailMix()) // Works with VendingMachine<TrailMix>
service(VendingMachine(GummyBears()) // Works with VendingMachine<GummyBears>``````

A star-projection looks like the original parameterized type, but:

• Anywhere that the type parameter was used in an in-position, it’ll be replaced with the `Nothing` type.
• Anywhere that the type parameter was used in an out-position, it’ll be replaced with the type parameter’s upper bound. Remember - if no upper bound was specified, it’ll be `Any?` by default.

In summary, star-projections are useful when you want to accept any instance of a generic type, regardless of its type arguments.

## Variance in the Standard Library

Now that we’ve learned all about covariance, contravariance, variance modifiers, and type projections, we can better understand why certain types in the standard library work the way that they do. We opened this chapter by presenting the fact that `MutableList<Cow>` is a subtype of `List<Cow>`, but it’s not a subtype of `MutableList<FarmAnimal>`. Why is this?

• `MutableList<Cow>` is a subtype of `List<Cow>` because `MutableList` extends the `List` interface. That’s just regular interface inheritance.
• `MutableList<Cow>` is not a subtype of `MutableList<FarmAnimal>` because it allows you to both read and modify its elements, which means its type parameter appears in both the in-position and out-position. As a result, it can’t have a variance modifier on it. This is similar to our `VendingMachine` in Listing 19.26.

As we’ve seen, the collection types in Kotlin usually come in two flavors - a read-only type (e.g., `List`) and a mutable type (e.g., `MutableList`). The read-only types will have the `out` modifier on their type parameters, so `List<Cow>` will be a subtype of `List<FarmAnimal>`. However, the mutable types won’t have any variance modifiers, so `MutableList<Cow>` will not be a subtype of `MutableList<FarmAnimal>`. As you now know, though, you can use a type projection to work around this when it makes sense!

## Summary

Enjoying this book?
Pick up the Leanpub edition today!

See the book on Leanpub

As Parker sat on a bench, watching the families running around in the park, he thought about the differences in the vending machines that Vinnie had installed. Now that the two of them understood which vending machines would satisfy the contract, they both felt much more comfortable about any replacements they might need to make in the future. Vinnie walked up to the vending machine and inserted a dime. He turned to Parker, who was still sitting on the bench. “Want a snack?” he asked him.

Congratulations on working your way through this chapter! Here’s a recap of what we learned:

• How covariance describes the relationship between a type and its function return types.
• How contravariance describes the relationship between a type and its function parameter types.
• How we can use variance modifiers on type parameters to create declaration-site variance.
• How we can use variance modifiers on type arguments to create use-site variance.
• How collection types in the standard library use variance modifiers.

Keep playing with these concepts in your Kotlin projects to help solidify your understanding! In the next chapter, we’ll introduce the very exciting topic of coroutines! See you then!

1. The goal of this chapter is to explain generic variance, so these three rules are focused on the structure of the types. More strictly speaking, though, the behaviors of the classes should be compatible, as well. (See Liskov, B., & Wing, J. M. (1994). A behavioral notion of subtyping. ACM Transactions on Programming Languages and Systems, 16(6), 1811-1841). Also, Meilir Page-Jones observes these three rules more generally through the lens of preconditions, postconditions, and class invariants. (Page-Jones, M. (2000). Fundamentals of Object-Oriented Design in UML. Addison-Wesley Professional. p. 283). ↩︎

2. Since the return type of `purchase()` varies depending on the type argument, we can’t just use `randomSnack()` any more - the snack has to have the same type as the type argument. For example, in `VendingMachine<CandyBar>`, the `purchase()` function must return a `CandyBar`, not a `Snack`. Setting this value in the constructor is a simple way to do this. If you prefer, you could change the `snack` property to a function type that constructs new snacks. e.g., `private val snack: () -> T`↩︎

3. The word “projection” in the term “type projection” is technically rooted in mathematics, but the idea is the same. ↩︎