PHP拒绝在这种简单情况下接受返回类型的原因是什么?

9
在PHP 7.1.4中,使用严格类型,我有一个简单的面向对象设置,涉及一些接口和一些实现这些接口的类。下面的例子,正如您所期望的那样,可以正常工作。
declare(strict_types=1);

interface Loginable {
  public function login();
}

interface Upgradeable {
  public function upgrade(): Loginable;
}

class Person implements Upgradeable {
  function upgrade(): Loginable {
    return new PersonAccount();
  }
}

class PersonAccount implements Loginable {
  public function login() {
    ;
  }
}

注意,Upgradable接口内的升级函数需要返回类型为Loginable的接口。在本例中,Person类内的upgrade方法指定了Loginable接口作为其返回类型以匹配接口规定。
然而,如果我现在尝试更精确地指定Person类的upgrade方法的返回类型,我会遇到致命错误。
class Person implements Upgradeable {
  function upgrade(): PersonAccount {
    return new PersonAccount();
  }
}

请注意,我在这里要实现的目标是指定升级方法将返回实现类所实现的接口所需的返回类型的对象。这对我来说似乎非常合理和正确,然而,PHP会报错:
致命错误:Person::upgrade()声明:PersonAccount必须与Upgradeable::upgrade(): Loginable兼容[...]
正如由yivi在https://stackoverflow.com/a/49353076/9524284中指出的那样,我想要实现的是不可能的。
如果涉及到扩展类,我会接受PHP的抱怨,因为扩展类可以覆盖原始方法,这样就无法保证正确的返回类型。但是,在上述情况下,没有扩展类,只有接口的实现,其整个目的就是明确保证正确的实现。
请阐明PHP拒绝接受上述声明返回类型方式背后的原因!

1
这个问题已经在 PHP 7.4 中得到了修复。 - bishop
1个回答

8
你正在描述一种类型推理功能,称为协变性,它本身是Liskov替换原则的结果。
截至PHP 7.4,这个功能按照你的期望工作。(有关详细信息,请参见实现该行为的RFC。)
在此之前,这个问题已经在内部讨论过了。正如其中一次对话中所说的那样:
如果某个实现比接口定义的要求更好,那么它应该能够实现该接口。
所以,是的,PHP应该允许您所描述的协变。但要认识到:并不是PHP拒绝实现协变,而是PHP尚未实现协变。虽然存在一些技术障碍,但它们并非无法克服。正如核心维护者在同一线程中所说的那样:
这是可行的,只是还没有实现。
如果您想提出RFC和PR,请这样做。在那之前,这仅仅是不幸的现状,属于不断发展的PHP对象系统。

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