为什么方法声明不兼容?

时间:2012-12-14 03:08:39

标签: php oop inheritance

为什么我收到此错误:

Fatal error: Declaration of ConcreteFooMapper::load() must be compatible with that of AbstractFooMapper::load() on line 18

来自此代码:

<?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) {

    }
}
?>

我最初的想法是,这是一个错误; 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!';
}
?>

打印 w00t!表示 ConcreteFoo Foo 的一个实例。

有关此行为是否正确的任何见解?

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:2)

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

请参阅see in details heresee here too

这是正确的行为\逻辑。

check here抽象类型非常有用,因为它们可用于定义和实施协议;实现协议的所有对象必须支持的一组操作。

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

此外,如果您使用相同的签名,它确实不是问题,导致所有类\ type信息可用。请检查以下代码

<?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)  

?>

答案 2 :(得分:0)

有趣的是,您可以使用构造函数来增强谓词:

// ABuilder.php

abstract class ABuilder
{
    public function __construct(BaseApi $api = null)
    {
        $this->api = $api;
    }

    public static function fromArray(array $data, BaseApi $api = null)
    {
        $entity = new static($api);

        // ...

        return $entity;
    }
}

// ContactBuilder.php
class Contact extends ABuilder
{
    // here, you cannot change signature of ::fromArray to require ContactApi
    // (which extends/implements BaseApi), like in this example:
    public static function fromArray(array $data, ContactApi $api = null) {}

    // but you can specify ContactApi in the constructor arguments:
    public function __construct(ContactApi $api = null) {}
}

因此,当您尝试使用错误的Api实现初始化ContactBuilder类(甚至通过::fromArray()结构)时,也会收到类似以下内容的正确的Type错误消息:

传递给Builder / Contact :: __ construct()的参数1必须是 ContactApi的实例或为null,指定了LeadApi的实例