> For the complete documentation index, see [llms.txt](https://harmeetsingh.gitbook.io/scala-type-system/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://harmeetsingh.gitbook.io/scala-type-system/phase-i/chapter-10-variance/co-variance.md).

# Co-Variance

**Co-Variance** is not new for **Scala** and **Java** developers. This already exists while we overriding a function in subclasses we can define covariance return type in the method signature as below:

```scala
class ShoppingCart {
   def bucket(apple : Apple): Fruit = apple
}

class FruitCart extends ShoppingCart {
   override def bucket(apple : Apple): Apple = apple // valid override method
}
```

In the above code, we are **overriding** the method `bucket` and defining return type `Apple` instead of `Fruit`, which is called **Co-Variance** return type. The compiler allows us to define a subclass in the override definition because according to the **polymorphism** we can assign a subclass object to its supertype reference. Let’s take an example:

```scala
val shoppingCart : ShoppingCart = new FruitCart
val fruit : Fruit = shoppingCart.bucket(new Apple) 
/* At runtime this calls to FruitCart bucket which returns Apple and because of 
polymorphism we can assign apple object to Fruit without any problem */
```

According to above example, it doesn’t matter `shoppingCart.bucket(...)` return which `Fruit`, because `Fruit` is a superclass of all fruits according to the polymophism we can always catch any fruits objects(like `Apple`, `Orange`, etc) via `Fruit` reference variable.&#x20;

#### Note:  We can use the co-variance return type in Java from version 5 onwards.

The way we are declaring the subclass, in override method signature is called co-variance return type, something similar we are performing in parameterized type co-variance. As shown in the **Diagram 10.1**, with the help of co-variance we can pass subclass parameterize type to superclass parameterize type. As per our example, we can pass or assign the object of `Garage[Lamborghini]` **(Garage of Lamborghini)** or `Garage[Jaguar]` **(Garage of Jaguar)** where it accepts `Garage[Car]` **(Garage of Car)**  method argument  or a reference variable.

Again, remember one thing carefully, **co-variance** defines the relationship between `Garage[Lamborghini]` **(Garage of Lamborghini)** with `Garage[Car]` **(Garage of Car)** on the basis of passed type paramter. For creating a co-variance type we need to define one special syntax like below:&#x20;

```scala
class Garage[+T](val car : T) {
	def washTheCar : T =  car
}

val lamborginiGarage = new Garage[Lamborghini](new Lamborghini)
val jaguarGarage = new Garage[Jaguar](new Jaguar)

def carGarage(gaurage : Garage[Car]) = gaurage.washTheCar

carGarage(lamborginiGarage) // it compiles and run successfully
res0: Car = Lamborghini@5fb3111a

carGarage(jaguarGarage) //it compiles and run successfully
res1: Car = Jaguar@1c2dd89b
```

**Note: plus symbol (+) is only allow ot use during define the types( like `Class`, `Traits`), you cannot use with variables and generics method.**

In our example, we successfully created co-variance type `Garage[+T]` which enables `Garage[Car]` inheritance hierarchy, where we can pass any subtype of `Garage[Car]`, and those are `Garage[Lamborghini]` and `Garage[Jaguar]`. Method `carGarage(Garage[Car])` accepts subtype of `Garage[Car]` objects.

Even, if some methods accept `Garage[Lamborghini]` or `Garage[Jaguar]` parameter, you can pass subclass of  `Garage[Lamborghini]` or `Garage[Jaguar]` like `Garage[LamborghiniUrus]` or `Garage[JaguarXF]`, but you can’t pass supertype like `Garage[Car]` to those methods because co-variance only accepts same or sub-hierarchy.

**Co-Variance parameters have one important catch or you can say rule which we discuss below:**\
While creating a generic type with the co-variance flag **(+),** the positions of the type parameter are only valid at methods return type location and variable datatype position as below:

```scala
class Garage[+T](val car : T) { // valid position of type parameter T
	def washTheCar : T =  car // valid position of type parameter T
	def repairTheCar(car : T): Car // invalid location of type parameter T
}
```

### Co-Variance FAQ

#### **Why does co-variance allow declaring the type parameter at method return type position, not in method arguments position?**

Before moving to the answer to this question, again go back and reminds our variance secrets.&#x20;

When we creating an object of `Garage[Car]` just assume something happens under the hood like below code:

```scala
class Garage[Car](val car: Car){
	def washTheCar: Car = car
}
```

When we creating an object of `Garage[Lamborghini]` just assume something happens under the hood like below code:

```scala
class Garage[Lamborghini](val car: Lamborghini){
	def washTheCar: Lamborghini = car
}
```

something similar with `Garage[Jaguar]` as well.&#x20;

We have a method called `carGarage(Garage[Car])` which accepts an object of `Garage[Car]` type as an argument which is **co-variance**. When we pass the object of `Garage[Lamborghini]` compiler checks, rule called **Liskov Substitution Principle(LSP)**, where we can replace the object of supertype with subtype. Let's take an example:&#x20;

![](https://lh4.googleusercontent.com/8ci2T32rP5iBS2u7a0uhGEggi39DIFiPXN4RyIEkzALGybkS5e7aIweUgKet2lNQC2pVHl6mYFtK_N5D53wSzewioYF8bPjQ_s6zvZknu8M6LNOSLH4q-El6EZRLsqAwY5b664zQ) **Below code never compile, this is for explanation purpose only.**

```scala
class Garage[+T](val car : T) { 
	def washTheCar : T =  car 
	def repairTheCar(car : T): Car // Let’s assume T is valid here.
}

// while creating a Garage[Car] object
class Garage[Car](val car : Car) { 
	def washTheCar : Car =  car 	
    def repairTheCar(car : Car): Car
}

// while creating a Garage[Lamborgini] object
class Garage[Lamborgini](val car : Lamborgini) { 
	def washTheCar : Lamborgini =  car 	
    def reparTheCar(Lamborgini : Lamborgini): Car 
}

def carGarage(garage: Garage[Car], car : Car) = {
	garage.repairTheCar(car)
}
```

Okay, let’s suppose this code is valid **what are the chances we will face runtime exception?** There are many, in method `carGarage(Garage[Car], Car)` If someone pass `Garage[Lamborgini]` type object and `Jaguar` type object as parameters, there is the chance we will get a runtime exception because at runtime actual method  is `repairTheCar(Lamborgini)` and we are passing `Jaguar` object, the first problem is `ClassCaseException`, **Second problem**, suppose some internal conversion happens it converts `Jaguar` to `Lamborgini` but we lose the value of `Lamborgini` features and there is a possibility we are using some  those `Lamborgini` particular features within the method  `repairTheCar(Lamborgini)` which are not available in converted `Lamborgini` type which generates runtime exception:&#x20;

```scala
val lamborginiGarage = new Garage[Lamborghini](new Lamborghini)
carGarage(lamborginiGarage, new Jaguar) // something bad during runtime
```

This whole scenario violates the **LSP** where we do not completely replace subtype in the place of supertype in case of `repairTheCar(car: T)` method call so that’s why the compiler does not allow us to define the type parameter at method argument level.&#x20;

#### &#x20;W**hy does co-variance allow type parameter at the location of variable datatype?**

There is a simple answer because in Scala variables and methods are handled by the same namespace.&#x20;

#### Is there any way to using the type parameter at the method argument location?

Yes, there is a way, but this is not as aspected. In [chapter 9th](/scala-type-system/phase-i/chapter-9-type-constraints.md#greatest-lower-bound-greater-than) we have an idea about type constraints where we are using **Least Upper Bound** and **Greatest Lower Bound.** For using type argument in co-varaince as a method parameter than **Greatest Lower Bound** comes into the picture. Let's take an example:&#x20;

```scala
class Garage[+T] {
    def repairTheCar[U >: T](car: U): Any = car
}
```

In method `repairTheCar(car: U)` we defined **Lower Bound** means, the **U** type is greater than or equals to type **T** means **U** could be `Any` type but not lower than passed **T** type parameter. Under the hood some thing happen as below:&#x20;

```scala
class Garage[Lamborghini] {
	def repairTheCar[Any >: Lamborghini](car: Any): Any = car // Suppose something similar happens internaly
}
```

When we create an object of `Garage[Lamborghini]` type and passed to our method `carGarage(Garage[Car], Car)`, everything is working fine.&#x20;

```scala
def carGarage(garage: Garage[Car], car : Car) = {
	garage.repairTheCar(car)
}

carGarage(lamborginiGarage, new Lamborghini) // Compiles and run successfully
res2: Any = Lamborghini@2941631f

carGarage(lamborginiGarage, new Jaguar) // compiles and run successfully
res3: Any = Jaguar@e2498a3
```

The `carGarage(lamborginiGarage, new Jaguar)` method call is compiled and run successfully because during the compilation, compilers know, `repairTheCar(Any)` method accepts `Any` type parameter if someone can pass any subtype of like `Jaguar` with `Garage[Lamborghini]`, there is no issue because `repairTheCar(Any)` can only use `Any` type features no there specific one, which are available in both `Lamborghini` and `Jaguar` objects.&#x20;

#### Why does compiler not allow to pass supertype object in co-variance like \`Garage\[FourWheeler]\` and generates compile time error?

For answering the question let’s take an example:&#x20;

```scala
class FourWheeler
class Car extends FourWheeler
class Lamborghini extends Car
class Jaguar extends Car

class Garage[+T](val car : T) {
    def washTheCar: T = car
}

def carGarage(garage: Garage[Car]): Car = {
	garage.washTheCar
}

val lamborginiGarage = new Garage[Lamborghini](new Lamborghini)
carGarage(lamborginiGarage, new Lamborghini) // Compiles successfully

val carGarage = new Garage[Car](new Car)
carGarage(carGarage, new Car) // Compiles successfully

val fourWheelerGarage = new Garage[FourWheeler](new FourWheeler)
carGarage(fourWheelerGarage, new FourWheeler) // Compile time error
```

While we are passing `Garage[FourWheeler]` to `carGarage(Garage[Car], Car)` method, the compiler gives us an error because the compiler knows, `Garage[Car]` maybe contains some method, which returns `Car` type object, but in `Garage[FourWheeler]` the same method must return `FourWheeler` type object, as per hierarchy `FourWheeler` is a superclass of `Car` and as per the **polymorphism** it is not valid to catch the superclass object to subclass. Let's elaborate this via code:&#x20;

![](https://lh4.googleusercontent.com/8ci2T32rP5iBS2u7a0uhGEggi39DIFiPXN4RyIEkzALGybkS5e7aIweUgKet2lNQC2pVHl6mYFtK_N5D53wSzewioYF8bPjQ_s6zvZknu8M6LNOSLH4q-El6EZRLsqAwY5b664zQ) **Below code is only used to demonstrate under the hood assumption.**&#x20;

```scala
// while creating Garage[Lamborghini]
class Garage[Lamborghini](val car : Lamborghini) {
    def washTheCar: Lamborghini = car
}

// while creating Garage[Car]
class Garage[Car](val car : Car) {
    def washTheCar: Car = car
}

// while creating Garage[FourWheeler]
class Garage[FourWheeler](val car : FourWheeler) {
    def washTheCar: FourWheeler = car
}

val car: Car = carGarage(lamborginiGarage, new Lamborghini)
// returns Lamborghini which is valid

val car: Car = carGarage(carGarage, new Car)
// returns Lamborghini which is valid

val car: Car = carGarage(fourWheelerGarage, new FourWheeler) // suppose this compiles
```

While `carGarage` method call `washTheCar(car)` method as per implementation, it returns `FourWheeler` type object, which is not possible to assing to the sub-class reference `Car` type that's why in co-varaince super types are not allow.
