## Covarianza y Contravarianza

En PHP 7.2.0, se introdujo la contravarianza parcial mediante la eliminación de las restricciones de tipo en los parámetros de un método hijo. A partir de PHP 7.4.0, se añadió soporte completo de covarianza y contravarianza.

La covarianza permite que el método hijo devuelva un tipo más específico que el tipo de devolución del método de su padre. Mientras que la contravarianza permite que un tipo de parámetro sea menos específico en un método hijo, que el de su padre.

Una declaración de tipo covarianza se considera más específica en el siguiente caso:

- Se elimina un tipo de un tipo de unión
- Se añade un tipo a un tipo de intersección
- Un tipo de clase se cambia a un tipo de clase hijo
- iterable es cambiado a array o Traversable

Una clase de tipo se considera menos específica si ocurre lo contrario.

### Covarianza

Para ilustrar cómo funciona la covarianza, una simple clase de padre abstracta, Animal es creada. Animal se extenderá a las clases hijo, Cat, y Dog.

`<?php
abstract class Animal
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
abstract public function speak();
}
class Dog extends Animal
{
public function speak()
{
echo $this->name . " barks";
}
}
class Cat extends Animal
{
public function speak()
{
echo $this->name . " meows";
}
}`

Observe que no hay ningún método que devuelva valores en este ejemplo. Unas pocas fábricas que devuelven un nuevo objeto de tipo clase Animal, Cat, o Dog.

`<?php
interface AnimalShelter
{
public function adopt(string $name): Animal;
}
class CatShelter implements AnimalShelter
{
public function adopt(string $name): Cat // instead of returning class type Animal, it can return class type Cat
{
return new Cat($name);
}
}
class DogShelter implements AnimalShelter
{
public function adopt(string $name): Dog // instead of returning class type Animal, it can return class type Dog
{
return new Dog($name);
}
}
$kitty = (new CatShelter)->adopt("Ricky");
$kitty->speak();
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$doggy->speak();`

El resultado del ejemplo sería:

    Ricky meows
    Mavrick barks

### Contravarianza

Continuando con el ejemplo anterior con las clases Animal, Cat, y Dog, una clase llamada Food y AnimalFood serán incluidas, y un método eat(AnimalFood $food) es añadido a la clase abstracta Animal.

`<?php
class Food {}
class AnimalFood extends Food {}
abstract class Animal
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function eat(AnimalFood $food)
{
echo $this->name . " eats " . get_class($food);
}
}`

Para ver el comportamiento de la contravención, el método eat se sobrescribe en la clase Dog para permitir cualquier tipo de objeto Food. La clase Cat permanece sin cambios.

`<?php
class Dog extends Animal
{
public function eat(Food $food) {
echo $this->name . " eats " . get_class($food);
}
}`

El siguiente ejemplo mostrará el comportamiento de la contravarianza.

`<?php
$kitty = (new CatShelter)->adopt("Ricky");
$catFood = new AnimalFood();
$kitty->eat($catFood);
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$banana = new Food();
$doggy->eat($banana);`

El resultado del ejemplo sería:

    Ricky eats AnimalFood
    Mavrick eats Food

¿Pero qué pasa si el (gatito) $kitty trata de (comer) eat la $banana?

`$kitty->eat($banana);`

El resultado del ejemplo sería:

    Fatal error: Uncaught TypeError: Argument 1 passed to Animal::eat() must be an instance of AnimalFood, instance of Food given