Kovarianz und Kontravarianz
Mit PHP 7.2.0 wurde teilweise Kontravarianz eingeführt, indem
Typeneinschränkungen bei Parametern von Kindmethoden entfernt wurden.
Mit PHP 7.4.0 wurde dann vollständige Unterstützung für Kovarianz und
Kontravarianz eingeführt.
Kovarianz erlaubt es den Methoden eines Kindes, einen spezifischeren Typen
als die Elternmethode für den Rückgabewert zurückzugeben.
Die Kontravarianz erlaubt einen weniger spezifischen Parametertyp in einer
Kindmethode als in der Elternmethode.
Eine Typdeklaration wird in den folgenden Fällen als spezifischer angesehen:
Falls das Gegenteil zutrifft, wird ein Klassentyp als weniger
spezifisch angesehen.
Kovarianz
Um Kovarianz zu illustrieren, wird eine einfache abstrakte Elternklasse
Tier erzeugt. Tier wird von seinen
Kindern Katze und Hund beerbt.
Beachtenswert ist, dass keine der Methoden hier einen Wert zurückgibt. Es werden nun
ein paar Factories hinzugefügt, die ein neues Objekt der Klassen Tier,
Katze oder Hund erzeugen.
Kontravarianz
Um das vorherige Beispiel mit den Klassen Tier,
Katze und Hund fortzusetzen,
werden nun die Klassen Nahrung sowie Tierfutter
definiert, sowie auch eine Methode iss(Tierfutter $futter) zur
abstrakten Klasse Tier hinzugefügt.
Um das Verhalten der Kontravarianz zu sehen, wird nun die Methode
iss in der Klasse Hund überschrieben,
um jedes Objekt der Klasse Nahrung zuzulassen. Die Klasse
Katze bleibt unverändert.
Das folgende Beispiel zeigt das Verhalten der Kontravarianz.
Varianz der Eigenschaften
Standardmäßig sind Eigenschaften weder kovariant noch kontravariant, d. h.
sie sind invariant.
Das bedeutet, dass sich ihr Typ in einer Kindklasse überhaupt nicht ändern
darf.
Der Grund dafür ist, dass "get"-Operationen kovariant und "set"-Operationen
kontravariant sein müssen.
Die einzige Möglichkeit für eine Eigenschaft, beide Anforderungen zu
erfüllen, besteht darin, invariant zu sein.
Seit PHP 8.4.0 ist es mit der Einführung von abstrakten Eigenschaften (auf
einer Schnittstelle oder einer abstrakten Klasse) und
virtuellen Eigenschaften
möglich, eine Eigenschaft zu deklarieren, die nur eine get- oder
set-Operation hat.
Folglich können abstrakte Eigenschaften oder virtuelle Eigenschaften, die
nur eine "get"-Operation benötigen, kovariant sein.
Ebenso kann eine abstrakte Eigenschaft oder eine virtuelle Eigenschaft, die
nur eine "set"-Operation benötigt, kontravariant sein.
Sobald eine Eigenschaft jedoch sowohl eine get- als auch eine set-Operation
hat, ist sie für weitere Erweiterungen nicht mehr kovariant oder
kontravariant.
Mit anderen Worten: Sie ist nun invariant.
Beispiel #1 Varianz des Eigenschaftstyps
<?php
class Animal {}
class Dog extends Animal {}
class Poodle extends Dog {}
interface PetOwner
{
// Es wird nur eine get-Operation benötigt, so dass diese kovariant
// sein kann.
public Animal $pet { get; }
}
class DogOwner implements PetOwner
{
// Dies kann ein restriktiverer Typ sein, da die "get"-Seite immer ein
// Animal zurückgibt. Da es sich jedoch um eine native Eigenschaft handelt,
// können Kinder dieser Klasse den Typ nicht mehr ändern.
public Dog $pet;
}
class PoodleOwner extends DogOwner
{
// Dies ist NICHT ZULÄSSIG, da für DogOwner::$pet sowohl get- als auch
// set-Operationen definiert sind und benötigt werden.
public Poodle $pet;
}
?>