PHP:覆盖抽象方法出现错误

3

我认为php的面向对象编程实现存在问题。

编辑:考虑一个更具有说明性的例子:

abstract class Animal {

    public $name;

    // public function Communicate(Animal $partner) {}     // Works
    public abstract function Communicate(Animal $partner); // Gives error
}

class Panda extends Animal {

    public function Communicate(Panda $partner) {
        echo "Hi {$partner->name} I'm a Panda";
    }

}

class Human extends Animal {

    public function Communicate(Human $partner) {
        echo "Hi {$partner->name} I'm a Human";
    }

}

$john = new Human(); $john->name = 'John';
$mary = new Human(); $mary->name = 'Mary';
$john->Communicate($mary); // should be ok

$zuzi = new Panda(); $zuzi->name = 'Zuzi';
$zuzi->Communicate($john); // should give error

问题在于当Animal::Communicate是一个抽象方法时,PHP会提示以下方法是非法的:
"public function Communicate(Panda $partner)" "public function Communicate(Human $partner)"
但是当Animal::Communicate不是抽象方法,而是具有零实现时,PHP认为这些方法是合法的。因此,在我看来,这并不正确,因为我们在两种情况下都进行了重写,并且这两种情况是相等的,所以这似乎是一个bug...
请考虑以下代码:
Framework.php
namespace A
{
    class Component { ... }

    abstract class Decorator {
        public abstract function Decorate(\A\Component $component);
    }
}

Implementation.php

namespace B
{
    class MyComponent extends \A\Component { ... }
}

MyDecorator.php

namespace A
{
    class MyDecorator extends Decorator {
        public function Decorate(\B\MyComponent $component) { ... }
    }
}

以下代码在MyDecorator.php中出现错误,提示:
致命错误:MyDecorator :: Decorate()的声明必须与A \ Decorator :: Decorate()兼容,在MyDecorator.php的第...行
但是,当我将Framework.php :: Decorator类更改为以下实现时:
    abstract class Decorator {
        public function Decorate(\A\Component $component) {}
    }

问题消失了。
4个回答

4
我不确定(没有测试;),但您声明了这个抽象函数:
public abstract function Decorate(\A\Component $component);

所以你应该像这样完全实现它。但你却做成了这样:
public function Decorate(\B\MyComponent $component) { ... }

这两者不同。您能否尝试将其更改为\A\Component

对于所有评论:事实是,这段PHP“运行”。

namespace A
{
    class Component { }

    abstract class Decorator {
        public abstract function Decorate(\A\Component $component);
    }
}
namespace B
{
    class MyComponent extends \A\Component { }
}
namespace A
{
    class MyDecorator extends Decorator {
        public function Decorate(\A\Component $component) {}
    }

}

而这个则不行:
<?php
namespace A
{
    class Component { }

    abstract class Decorator {
        public abstract function Decorate(\A\Component $component);
    }
}
namespace B
{
    class MyComponent extends \A\Component { }
}
namespace A
{
    class MyDecorator extends Decorator {
        public function Decorate(\B\MyComponent $component) {}
    }

}
?>

使用以下代码时出现错误:PHP致命错误:A \ MyDecorator :: Decorate()的声明必须与A \ Decorator :: Decorate()在第18行兼容

现在,您可以尽情讨论该如何或不应该,但这是代码的问题。

因此,为了满足自己的好奇心:这也是非法的:

<?php
    class Component { }

    abstract class Decorator {
        public abstract function Decorate(Component $component);
    }

    class MyComponent extends Component { }
    class MyDecorator extends Decorator {
        public function Decorate(MyComponent $component) {}
    }


?>

不是命名空间或其他什么问题,只是看起来不太合法。


2
这种模式限制了类型更为具体,这不应该是一个问题,因为 \B\MyComponent 扩展了 \A\Component,这意味着 \B\MyComponent 本来就是 \A\Component。 - Lu4
1
但是你的抽象使你定义了一个接受所有\A\Component参数的函数,而你的实现只接受\B\MyComponent。这意味着你没有实现超类所“要求”的函数。 - Nanne
不错,Lu4是正确的,因为更具体的类必须是其超类的子类型,并满足所有存在于其超类中的要求。这被称为Liskow替换原则,是面向对象编程的基本规则... - Paul
也许是命名空间的问题,也许你在 PHP 中没有遵循 Liskow 的替换原则,但这仍然是它无法工作的原因。请参见我的答案中的示例编辑。 - Nanne
好的,我再也没有看到任何问题了。你必须按照我在PHP中发布的方式进行操作(因此不使用派生类)。这可能是一个错误,但我们都同意这就是现状,对吧?我真的不知道还要添加什么了... - Nanne
对于任何在以后遇到此问题的人,这仍然适用于PHP 7.1。 - Eaton Emmerich

2

问题在于类型提示通常允许泛化,但当方法是抽象的时却不允许它进行特定化,因此我怀疑自从2006年以来他们已经实现了这个功能,但忘记了某些东西,导致当方法是抽象的时候出现错误。 - Lu4

1

这与它是抽象的无关,而与类型提示有关。该方法的两个定义不兼容,因为您明确设置了参数的类型为\A\Component,然后尝试使用\B\Component重载该方法,您不能这样做,因为它会更改方法签名。任何后续声明Decorate的必须使用与其父声明相同的类型提示,以使方法签名兼容。


1
是的,但是 \B\Component 扩展了 \A\Component,这意味着从面向对象编程的角度来看,签名是相等的。无论如何,当方法具有零实现时,它仍然可以工作;但是当我将其标记为抽象并删除零实现时,它会提示方法不兼容。我认为方法的实现不是方法签名兼容性的主题。 - Lu4
2
实际上,这是他们在PHP中搞砸的一个主题(像许多其他主题一样)...严格来说,在面向对象的语言中(事实上,PHP只有在相当长的时间内才是面向对象的),子类型的类型提示必须是可能的... - Paul

0
这可能会帮助到一些人,并且不算晚。 处理这样的情况最好的方法是使用接口。 请考虑以下内容;
<?php
    interface Componentor{}

    class Component implements Componentor { }

    abstract class Decorator {
        public abstract function Decorate(Componentor $component);
    }

    class MyComponent extends Component { }

    class MyDecorator extends Decorator {
        public function Decorate(Componentor $component) {}
    }

?>

用法:

<?php
    $c=new Component();
    //TODO ....blah blah blah...
    $decor=new MyDecorator();
    //TODO ....blah blah blah...
    $decor->Decorate($c);

?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接