为什么方法声明不兼容?

kul*_*hch 5 php oop inheritance

为什么我会收到此错误:

Fatal error: Declaration of ConcreteFooMapper::load() must be compatible with that of AbstractFooMapper::load() on line 18
Run Code Online (Sandbox Code Playgroud)

从这个代码:

<?php
interface Foo {
    public function foo();
}

class ConcreteFoo implements Foo {
    public function foo() {

    }
}

abstract class AbstractFooMapper {
    abstract public function load(Foo $entity, array $data);
}

class ConcreteFooMapper extends AbstractFooMapper {
    public function load(ConcreteFoo $entity, array $data) {

    }
}
?>
Run Code Online (Sandbox Code Playgroud)

我最初的想法是这是一个错误;PHP在评估方法声明时没有检测到ConcreteFoo实现了Foo。我认为这是因为当您运行此代码时:

<?php
interface Foo {
    public function foo();
}

class ConcreteFoo implements Foo {
    public function foo() {

    }
}

$foo = new ConcreteFoo();

if ($foo instanceof Foo) 
{
    print 'w00t!';
} 
else 
{
    print 'FAIL!';
}
?>
Run Code Online (Sandbox Code Playgroud)

它打印w00t!表示ConcreteFooFoo 的一个实例。

关于这种行为是否正确的任何见解?

Maj*_*ons 7

根据文档,类型提示必须完全匹配。

  • Php 运行正常。正如所写,该方法的某些重写可能接受仅实现“Foo”的方法,而其他方法可能只接受“ConcreteFooMapper”。那不是正确的多态性。覆盖方法可以键入“Foo”的提示,并且仍然接受“Conrete...”类型的参数。如果绝对必要,它可以在其代码中验证“Foo”实现者的类。 (3认同)
  • 强化扩展类中的谓词将违反里氏替换原则;这可能不是根本原因,但它对我有用:) (2认同)

Vad*_*dim 4

实现接口的类必须使用与接口中定义的完全相同的方法签名。不这样做将导致致命错误。对于扩展抽象类的类也有相同的规则。

请参阅此处详细信息也请参阅此处

这就是正确的行为\逻辑。

检查此处抽象类型很有用,因为它们可用于定义和强制执行协议;所有实现协议的对象都必须支持的一组操作。

如果我们假设您的代码将毫无异常地工作,那么我们会遇到以下问题:ConcreteFooMapper不能使用 some 的实例class ConcreteFoo2 implements Foo作为load方法的参数,但应该(通过抽象类定义)

另外,如果您使用相同的签名,这实际上不是问题,因为所有类\类型信息都可用。请检查以下代码

<?php
interface Foo {
        public function foo();
}

class ConcreteFoo implements Foo {
        public function foo() {
        }
}

abstract class AbstractFooMapper {
        abstract public function load(Foo $entity, array $data);
}

class ConcreteFooMapper extends AbstractFooMapper {
        public function load(Foo $entity, array $data) {
                var_dump($entity instanceof Foo);
                var_dump($entity instanceof ConcreteFoo);
        }

}

$someConcreteFoo = new ConcreteFoo();
$someFooMapper = new ConcreteFooMapper();

$someFooMapper->load($someConcreteFoo, array('somekey' => 'somevalue'));
// output
// bool(true) bool(true)  

?>
Run Code Online (Sandbox Code Playgroud)