gpt4 book ai didi

php - PHP 中抽象类和特征的类型协变

转载 作者:行者123 更新时间:2023-12-04 01:35:58 25 4
gpt4 key购买 nike

我正在开发一个 PHP (7.4) 库,需要使用特征来实现新功能,但我遇到了参数类型协方差的问题。

我有一个像这样的抽象父类:

<?php

abstract class ParentClass {
abstract public function parentMethod($param): bool;
}

?>

我还有一个特点:

<?php

trait MyTrait {
abstract public function traitMethod($param): bool;
}

?>

我在子类中同时使用类和特征:

<?php

class ChildClass extends ParentClass {

use MyTrait;

// implementation of the abstract methods

public function parentMethod(int $param): bool { // change parent method parameter type
// implementation
}

public function traitMethod(int $param): bool { // change trait method parameter type
// implementation
}
}

?>

这里的问题是我得到了这个错误:

Fatal error: Declaration of ChildClass::parentMethod(int $param): bool must be compatible with ParentClass::parentMethod($param): bool

看来我无法更改 parentMethod() 参数类型。如果我删除 parentMethod() 定义中的 int 类型,我不会收到错误!即使在 trait 方法上有一个特定的类型参数。

为什么我可以将协变参数类型用于 trait 抽象方法而不是抽象类方法?

最佳答案

Covariance 和 Contravariance 是与继承有关的概念,使用 trait 不是继承。

注意:上面的说法并不完全正确,我会在这个答案的最后解释原因。

来自PHP documentation

A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance.

为什么会看到此错误?

因为 int 不是 everything 的父类(super class)型,也不是代表任何类型的伪类型(将 int 替换为 mixed 在 PHP 8 中,看看会发生什么)。还有 Type widening不允许使用任意父类(super class)型(你只能省略类型)

例如,假设您这样定义父方法:

abstract public function parentMethod(int $param): bool;

类型扩展允许您在 ChildClass 中仅省略 $param 数据类型。

Contravariance, allows a parameter type to be less specific ina child method, than that of its parent

假设我们有另一个名为 C 的类,它扩展了 stdClass 并且我们将 parentMethod 定义为仅接受 C 类型的对象

class C extends stdClass {}

abstract class ParentClass
{
abstract public function parentMethod(C $param): bool;
}

现在在 ChildClass 中,如果我们实现 parentMethod 以接受 stdClass 类型的对象

public function parentMethod(stdClass $param): bool
{

}

这将起作用并且不会发出任何错误。

这就是逆变

#编辑至于你在评论中的问题

为什么可以在子类中键入实现的特征方法的参数?

因为特征是复制粘贴到一个类中,所以您不能对它们强加 OOP 规则。这就是为什么您可以覆盖特征中的 final 方法。

trait Foo
{
final public function method($var)
{
return $var;
}
}
class Bar
{
use Foo;
// "Override" with no error
final public function method($var)
{
return $var;
}
}

trait 中抽象方法的作用是强制显示类实现它们(类型和访问修饰符可能不同)

The PHP documentation

Caution A concrete class fulfills this requirement by defining aconcrete method with the same name; its signature may be different.

2020 年 10 月更新

从 PHP 8 开始,特征中抽象方法的行为发生了变化,现在具有不匹配签名的抽象方法将失败并出现 fatal error ,并且 LSP 规则将应用于它们。

好的,这是为什么?

整个变化始于 bug报告,并进行了一些讨论here

似乎在 PHP 8 之前存在某种行为冲突:

而且因为 abstract 本身表示契约(Contract),因此您所质疑的是合法的,现在使用 PHP 8 您会很高兴 https://3v4l.org/7sid7 :).

关于php - PHP 中抽象类和特征的类型协变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59562291/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com