- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在开发一个 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 不是继承。
注意:上面的说法并不完全正确,我会在这个答案的最后解释原因。
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 中抽象方法的作用是强制显示类实现它们(类型和访问修饰符可能不同)
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 规则将应用于它们。
好的,这是为什么?
似乎在 PHP 8 之前存在某种行为冲突:
而且因为 abstract
本身表示契约(Contract),因此您所质疑的是合法的,现在使用 PHP 8 您会很高兴 https://3v4l.org/7sid7 :).
关于php - PHP 中抽象类和特征的类型协变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59562291/
COW 不是奶牛,是 Copy-On-Write 的缩写,这是一种是复制但也不完全是复制的技术。 一般来说复制就是创建出完全相同的两份,两份是独立的: 但是,有的时候复制这件事没多大必要
我是一名优秀的程序员,十分优秀!