假设我们有以下两个类:
abstract class Foo {
public abstract function run(TypeA $object);
}
class Bar extends Foo {
public function run(TypeB $object) {
// Some code here
}
}
类TypeB扩展了TypeA类。
尝试使用它会产生以下错误消息:
Bar :: run()的声明必须与Foo :: run()的声明兼容
在涉及参数类型时,PHP真的会被破坏,还是我在这里忽略了这一点?
答案 0 :(得分:6)
您描述的行为称为covariance,PHP中根本不支持。我不知道内部,但我可能会怀疑PHP的核心在应用所谓的“类型提示”检查时根本不评估继承树。
顺便说一下,PHP也不支持contravariance这些类型提示(一种通常支持其他OOP语言的功能) - 最有可能的原因是上面提到的。所以这也不起作用:
abstract class Foo {
public abstract function run(TypeB $object);
}
class Bar extends Foo {
public function run(TypeA $object) {
// Some code here
}
}
最后还有一些信息:http://www.php.net/~derick/meeting-notes.html#implement-inheritance-rules-for-type-hints
答案 1 :(得分:2)
这似乎与大多数OO校长非常一致。 PHP与.Net不同 - 它不允许您覆盖类成员。 Foo
的任何扩展都应该滑入之前使用Foo
的位置,这意味着您无法放松约束。
简单的解决方案显然是删除类型约束,但如果Bar::run()
需要不同的参数类型,那么它实际上是一个不同的函数,理想情况下应该有不同的名称。
如果TypeA
和TypeB
有任何共同点,请将公共元素移动到基类并将其用作参数约束。
答案 2 :(得分:0)
我认为这是by design:定义其方法的基本行为是抽象定义的重点。
从抽象类继承时,父类声明中标记为abstract的所有方法都必须由子类定义;此外,必须使用相同(或限制较少)的可见性来定义这些方法。
答案 3 :(得分:0)
总是可以在代码中添加约束:
public function run(TypeA $object) {
assert( is_a($object, "TypeB") );
您必须记住或记录特定的类型限制。优点是它变成纯粹的开发工具,因为断言通常在生产服务器上关闭。 (实际上,这是在开发过程中发现的一类错误之一,而不是随机破坏生产。)
答案 4 :(得分:0)
问题中显示的代码无法在PHP中编译。如果确实这样做,则类Bar
将无法兑现其父级Foo
所作的保证,即不能接受TypeA
的任何实例,并且违反了Liskov Substitution Principle。>
当前相反的代码也不会编译,但是在预期于2019年11月28日发布的PHP 7.4版本中,下面的类似代码将使用新的contravariant arguments功能有效:
abstract class Foo {
public abstract function run(TypeB $object); // TypeB extends TypeA
}
class Bar extends Foo {
public function run(TypeA $object) {
// Some code here
}
}
所有条形图都为Foos,但并非所有的条形图都为Bars。所有的TypeB都是TypeA,但并非所有的TypeA都是TypeB。任何Foo都可以接受任何TypeB。那些也是Bar的Foos也将能够接受非TypeB TypeA。
PHP也将支持协变返回类型,其工作方式相反。
答案 5 :(得分:-1)
虽然您不能将整个类层次结构用作类型提示。您可以使用self
和parent
关键字在某些情况下强制执行类似的操作。
引用PHP-manual评论中的r dot wilczek at web-appz dot de:
<?php
interface Foo
{
public function baz(self $object);
}
class Bar implements Foo
{
public function baz(self $object)
{
//
}
}
?>
现在没有提到的是你也可以使用'parent'作为typehint。带接口的示例:
<?php
interface Foo
{
public function baz(parent $object);
}
class Baz {}
class Bar extends Baz implements Foo
{
public function baz(parent $object)
{
//
}
}
?>
Bar :: baz()现在将接受任何Baz实例。 如果Bar不是任何类的继承人(没有“扩展”),PHP将引发致命错误: '当前类范围没有父级时,无法访问父::。