Covariance and Contravariance
In PHP 7.2.0, partial contravariance was introduced by removing type restrictions
on parameters in a child method. As of PHP 7.4.0, full covariance and contravariance
support was added.
Covariance allows a child's method to return a more specific type than the return type
of its parent's method. Whereas, contravariance allows a parameter type to be less
specific in a child method, than that of its parent.
Covariance
To illustrate how covariance works, a simple abstract parent class, Animal
is created. Animal will be extended by children classes,
Cat, and Dog.
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";
}
}
]]>
Note that there aren't any methods which return values in this example. A few factories
will be added which return a new object of class type Animal,
Cat, or Dog.
adopt("Ricky");
$kitty->speak();
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$doggy->speak();
]]>
&example.outputs;
Contravariance
Continuing with the previous example with the classes Animal,
Cat, and Dog, a class called
Food and AnimalFood will be included, and
a method eat(AnimalFood $food) is added to the Animal
abstract class.
name = $name;
}
public function eat(AnimalFood $food)
{
echo $this->name . " eats " . get_class($food);
}
}
]]>
In order to see the behavior of contravariance, the
eat method is overridden in the Dog class to allow
any Food type object. The Cat class remains unchanged.
name . " eats " . get_class($food);
}
}
]]>
The next example will show the behavior of contravariance.
adopt("Ricky");
$catFood = new AnimalFood();
$kitty->eat($catFood);
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$banana = new Food();
$doggy->eat($banana);
]]>
&example.outputs;
But what happens if $kitty tries to eat the
$banana?
eat($banana);
]]>
&example.outputs;