为什么PHP允许你在构造函数上重载类型提示而不允许在方法上重载?

时间:2014-07-17 12:42:59

标签: php

假设你有这个:

<?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错误?

2 个答案:

答案 0 :(得分:0)

请阅读:Override method parameter with child interface as a new parameter

TL; DR:如果使用 narrower 类型重载方法参数类型,则会更改方法签名并使其与父方法不兼容。您不能用任意子类替换TypeUser的实例,因为重载的子类的方法签名与父类不兼容,因此没有给出用于调用此类实例的稳定的已知接口。

为什么它适用于构造函数?因为构造实例,您(通常)硬编码其类名:

$user = new SomeTypeUser($parameter);

这里没有含糊不清的构造函数所期望的参数,你确切知道你要实例化的是什么类。

但是,情况并非如此:

function (TypeUser $user) {
    $user->foo($something);
}

那么,$user是哪一个类的实例? SomeTypeUserSomeOtherTypeUser?完全不同的东西?是$user->foo()的签名foo(SomeType $type)还是foo(SomeOtherType $type)?以上代码是否会在运行时爆炸?谁知道......

答案 1 :(得分:-1)

PHP允许在inherted方法中重载参数,只要它们未定义为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()的声明必须与xy行上的/.../test.php中的Foo :: baz(ArrayObject $ array)兼容

虽然

abstract class Foo {
    public function baz(ArrayObject $array) {
    }
}

class Bar extends Foo {
   public function baz(DateTime $dateTime) {
   }
}

很好并且有效,因为baz方法不是接口定义方法的实现,也不是父类中的抽象方法。