📅  最后修改于: 2023-12-03 15:18:34.459000             🧑  作者: Mango
PHP作为一种面向对象的编程语言,在编写程序时经常会用到协变和逆变的概念,特别在泛型等领域使用较为频繁,本文就介绍PHP的协方差和逆变。
在PHP7.2之前,方法的返回值类型是不能修改的,而在PHP7.2之后,可以使用covariant返回类型来进行协变。 协变通过子类方法的返回值类型可以是父类方法返回值类型的的子类型来实现。
例如,有一个Animal类:
class Animal {}
有一个Dog类继承于Animal类:
class Dog extends Animal {}
如果Animal类有一个eat()方法,返回类型为Animal类:
class Animal {
public function eat(): Animal
{
}
}
由于Dog类是Animal类的子类,所以在Dog类中重写eat()方法时,返回类型可以改为Dog类:
class Dog extends Animal {
public function eat(): Dog
{
}
}
这就是协变的特性。
在PHP7.4之后,协变还可以应用于属性的类型声明,例如:
class Box
{
public function __construct(private Animal $animal) {}
public function getAnimal(): Animal
{
return $this->animal;
}
}
class DogBox extends Box
{
public function getAnimal(): Dog
{
return parent::getAnimal();
}
}
逆变和协变相反,通过父类方法的参数类型可以是子类方法参数类型的父类型来实现。
例如,有一个Animal类:
class Animal {}
有一个Dog类继承于Animal类:
class Dog extends Animal {}
如果Animal类有一个eat()方法,有一个参数,类型为Animal类:
class Animal {
public function eat(Animal $animal)
{
}
}
由于Dog类是Animal类的子类,所以当在Dog类中重写eat()方法时,参数类型可以改为Dog类:
class Dog extends Animal {
public function eat(Dog $dog)
{
}
}
这就是逆变的特性。
在PHP7.4之后,逆变还可以应用于函数的参数类型声明,例如:
interface Reader
{
public function read();
}
interface Writer
{
public function write(Animal $animal);
}
class AnimalReader implements Reader
{
public function read(): Animal {}
}
class DogWriter implements Writer
{
public function write(Dog $dog) {}
}
function test(Reader $reader, Writer $writer)
{
$writer->write($reader->read());
}
$reader = new AnimalReader();
$writer = new DogWriter();
test($reader, $writer);
上述示例中,函数test()接收一个Reader类型的参数$reader和一个Writer类型的参数$writer,并在函数内部将$reader所读取的内容传递给$writer进行写入。虽然$reader读取的内容类型为Animal,而$writer只能写入类型为Dog的对象,但由于Dog是Animal的子类,当$reader读取Animal对象并传递给$writer写入时,PHP会自动进行类型转换,将Animal对象转换为Dog对象。这就是逆变的特性。
协变和逆变是面向对象编程中的重要概念,在PHP中得到了较好的支持。协变可以简化代码,逆变则可以提高代码复用性。在实际开发中,需要根据具体情况选择合适的方式。