# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://harmeetsingh.gitbook.io/scala-type-system/phase-i/chapter-10-variance/co-variance.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
