Covariance et Contravariance
À partir de PHP 7.2.0, la contravariance partielle a été introduite en supprimant les restrictions de type sur les paramètres d'une méthode enfant. À partir de PHP 7.4.0, la covariance et la contravariance complètes ont été ajoutées.
La covariance permet à une méthode enfant de retourner un type plus spécifique que le type de retour de sa méthode parente. En revanche, la contravariance permet à un type de paramètre d'être moins spécifique dans une méthode enfant que dans celui de la méthode parente.
Une déclaration de type est considérée comme plus spécifique dans le cas suivant :
- Un type est retiré d'un type union
- Un type est ajouté à un type d'intersection
- Un type de classe est transformé en un type de classe enfant
- iterable est remplacé par array ou Traversable
Covariance
Pour illustrer le fonctionnement de la covariance, une simple classe parente abstraite, Animal est créée. Animal sera étendu par des classes enfants, Cat et 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";
}
}
Notez qu'il n'y a pas de méthodes qui renvoient des valeurs dans cet exemple. Quelques fabriques seront ajoutées et renverront un nouvel objet de classe de type Animal, Cat, ou Dog.
<?php
interface AnimalShelter
{
public function adopt(string $name): Animal;
}
class CatShelter implements AnimalShelter
{
public function adopt(string $name): Cat // au lieu de renvoyer le type de classe Animal, il peut renvoyer le type de classe Cat
{
return new Cat($name);
}
}
class DogShelter implements AnimalShelter
{
public function adopt(string $name): Dog // au lieu de renvoyer le type de classe Animal, il peut renvoyer le type de classe Dog
{
return new Dog($name);
}
}
$kitty = (new CatShelter)->adopt("Ricky");
$kitty->speak();
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$doggy->speak();
L'exemple ci-dessus va afficher :
Ricky meows Mavrick barks
Contravariance
En reprenant l'exemple précédent avec les classes Animal, Cat et Dog, deux classes appelées Food et AnimalFood sont incluses, et une méthode eat(AnimalFood $food) est ajoutée à la classe abstraite 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);
}
}
Afin de voir le comportement de la contravariance, la méthode méthode eat est surchargée dans la classe Dog afin d'autoriser n'importe quel objet de type Food. La classe Cat reste inchangée.
<?php
class Dog extends Animal
{
public function eat(Food $food) {
echo $this->name . " eats " . get_class($food);
}
}
L'exemple suivant montre le comportement de la contravariance.
<?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);
L'exemple ci-dessus va afficher :
Ricky eats AnimalFood Mavrick eats Food
Mais que se passe-t-il si $kitty essaie de manger (eat()) la banane ($banana) ?
$kitty->eat($banana);
L'exemple ci-dessus va afficher :
Fatal error: Uncaught TypeError: Argument 1 passed to Animal::eat() must be an instance of AnimalFood, instance of Food given
Version en cache
25/12/2024 07:02:10 Cette version de la page est en cache (à la date du 25/12/2024 07:02:10) afin d'accélérer le traitement. Vous pouvez activer le mode utilisateur dans le menu en haut pour afficher la dernère version de la page.Document créé le 30/01/2003, dernière modification le 26/10/2018
Source du document imprimé : https://www.gaudry.be/php-rf-language.oop5.variance.html
L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.
Références
Ces références et liens indiquent des documents consultés lors de la rédaction de cette page, ou qui peuvent apporter un complément d'information, mais les auteurs de ces sources ne peuvent être tenus responsables du contenu de cette page.
L'auteur de ce site est seul responsable de la manière dont sont présentés ici les différents concepts, et des libertés qui sont prises avec les ouvrages de référence. N'oubliez pas que vous devez croiser les informations de sources multiples afin de diminuer les risques d'erreurs.