假设你有:
<?php
interface Type
{}
class SomeType implements Type
{}
class SomeOtherType implements Type
{}
现在,如果你有一个抽象类,它有Type
接口作为构造函数依赖,像这样:
<?php
abstract class TypeUser
{
public function __construct(Type $type)
{}
}
为什么PHP允许像这样在具体类上重载构造函数:
<?php
class SomeTypeUser extends TypeUser
{
public function __construct(SomeType $type)
{}
}
class SomeOtherTypeUser extends TypeUser
{
public function __construct(SomeOtherType $type)
{}
}
但是它不允许你对方法做同样的事情,只有构造函数-如果你试图对非构造函数方法这样做,PHP会抛出一个Strict standards
错误?
请阅读:用子接口作为新参数覆盖方法参数
TL;DR:如果用窄的类型重载方法参数类型,方法签名将被更改,并使与父方法不兼容。你不能用任意的子类代替TypeUser
的实例,因为重载的子类的方法签名与父类的方法签名不兼容,因此没有稳定的,已知的接口来调用这样的实例。
为什么在构造函数中有效?因为要构造一个实例,您(通常)硬编码它的类名:
$user = new SomeTypeUser($parameter);
这里构造函数期望的参数没有歧义,您确切地知道要实例化的是哪个类。
然而,这里的情况并非如此:
function (TypeUser $user) {
$user->foo($something);
}
那么,$user
是什么类的实例呢?SomeTypeUser
吗?SomeOtherTypeUser
吗?完全不同的东西?$user->foo()
的签名是foo(SomeType $type)
还是foo(SomeOtherType $type)
?上面的代码是否会在运行时爆炸?谁知道…
PHP允许在继承方法中重载参数,只要它们不是定义为abstract
或属于该类扩展/实现的interface
。
interface Foo {
public function baz(ArrayObject $array);
}
class Bar implements Foo {
public function baz(DateTime $dateTime) {
}
}
或
abstract class Foo {
public abstract function baz(ArrayObject $array);
}
class Bar extends Foo {
public function baz(DateTime $dateTime) {
}
}
将导致众所周知的错误:
致命错误:Bar::baz()的声明必须与/…/test.php中的Foo::baz(ArrayObject $array)兼容
,
abstract class Foo {
public function baz(ArrayObject $array) {
}
}
class Bar extends Foo {
public function baz(DateTime $dateTime) {
}
}
很好,因为baz
方法不是接口定义方法的实现,也不是父类中的抽象方法。