Decla :: Specific()声明:必须与General :: method()兼容。 PHP的错误是错误的吗?

时间:2018-04-16 14:33:19

标签: php liskov-substitution-principle

我认为“......必须与...兼容”才能执行Liskov替换原则。但我不确定LSP是什么意思?

我有这样的代码:

class General
{
    public static function create(): General
    {
        return new static;
    }

    public function doSomething()
    {
        echo get_class($this) . ' speaking!' . PHP_EOL;
    }
}


class Specific extends General
{
    public static function create(): Specific
    {
        return parent::create();
    }
}


function doSomething(General $object)
{
    $object->doSomething();
}

doSomething(General::create());
doSomething(Specific::create());

产生:

  

PHP致命错误:声明特定:: create():具体必须与General :: create()兼容:General in ...

LSP通常被引用为:

  

使用指针或对基类的引用的函数必须能够   在不知情的情况下使用派生类的对象。

据我所知,这并没有违反。那么这里有什么问题?是否有一些与LSP无关的特殊限制?这是PHP中的错误吗?在不知情的情况下我做错了吗?

更新:我找到了这个帖子(Parameter type covariance in specializations)。我理解并完全同意那里的例子违反了LSP。但我的情况有所不同(事实上相反)。

2 个答案:

答案 0 :(得分:1)

http://php.net/manual/en/functions.returning-values.php

  

覆盖父方法时,子方法必须匹配父方法上的任何返回类型声明。如果父级没有定义返回类型,则子方法可以这样做。

您的Specific::create函数应指明General返回类型。

否则,为Specific::create编写的代码在运行General::create时可能会中断,因为它会收到不同的类。

答案 1 :(得分:0)

LSP说明方法参数必须是协变量,而返回值必须是协变量。

在您的情况下,您有满足LSP的协变返回类型。

问题出在PHP本身。此限制与LSP无关,只是早期版本的PHP尚未实现。

从PHP 7.2(7.4)开始,它现在完全支持LSP,即参数相反和返回值相关:substring

UPD。。但是您的代码还包含另一个问题:您的\Specific::create()方法必须返回Specific的实例(如您在其签名中所声明的那样),但它会尝试返回\General::create()返回的值表示为General(并且我们知道General的实例不是instanceof Specific)。这是相当误导的。例如。 PhpStorm会警告您:Return value is expected to be 'Specific', 'General' returned。 但是PHP不会对此引发错误。

可以通过在\General::create()中添加PhpDoc来解决此问题:

class General
{
    /**
     * @return static
     */
    public static function create(): General
    {
        return new static;
    }