[英]PHP 7 interfaces, return type hinting and self
更新:PHP 7.4 现在确实支持协变和逆变,这解决了这个问题中提出的主要问题。
我在 PHP 7 中使用返回类型提示时遇到了一些问题。我的理解是提示: self
意味着您打算实现 class 以返回自身。 因此,我在我的界面中使用: self
来表示这一点,但是当我尝试实际实现该界面时,我遇到了兼容性错误。
以下是我遇到的问题的简单演示:
interface iFoo
{
public function bar (string $baz) : self;
}
class Foo implements iFoo
{
public function bar (string $baz) : self
{
echo $baz . PHP_EOL;
return $this;
}
}
(new Foo ()) -> bar ("Fred")
-> bar ("Wilma")
-> bar ("Barney")
-> bar ("Betty");
预期的 output 是:
弗雷德·威尔玛·巴尼·贝蒂
我实际得到的是:
PHP 致命错误:Foo::bar(int $baz) 声明:Foo 必须与 iFoo::bar(int $baz) 兼容:第 7 行的 test.php 中的 iFoo
问题是 Foo 是 iFoo 的一个实现,据我所知,这个实现应该与给定的接口完全兼容。 我大概可以通过更改接口或实现 class(或两者)来通过名称而不是使用self
返回提示接口来解决这个问题,但我的理解是语义上的self
意味着“返回你刚刚调用的 class 的实例方法”。 因此,将其更改为接口在理论上意味着我可以返回实现该接口的任何实例,而我的意图是调用的实例将被返回。
这是 PHP 中的疏忽还是故意的设计决定? 如果是前者,是否有机会在 PHP 7.1 中修复它? 如果不是,那么正确的返回方式是什么,暗示您的接口希望您返回您刚刚调用方法进行链接的实例?
self
不是指实例,而是指当前类。 接口无法指定必须返回相同的实例- 以您尝试的方式使用self
只会强制返回的实例属于同一类。
也就是说,PHP 中的返回类型声明必须是不变的,而您正在尝试的是协变的。
你对self
使用相当于:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
这是不允许的。
继承期间声明的返回类型的执行是不变的; 这意味着当子类型覆盖父方法时,子类型的返回类型必须与父方法完全匹配并且不能省略。 如果父级未声明返回类型,则允许子级声明返回类型。
...
该 RFC 最初提出协变返回类型,但由于一些问题改为不变。 可以在将来的某个时候添加协变返回类型。
至少目前你能做的最好的是:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : iFoo {...}
}
它也可以是一个解决方案,您没有在接口中明确定义返回类型,只在 PHPDoc 中定义,然后您可以在实现中定义某些返回类型:
interface iFoo
{
public function bar (string $baz);
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
如果你想从接口强制,该方法将返回对象,但对象的类型不会是接口的类型,而是类本身,那么你可以这样写:
interface iFoo {
public function bar(string $baz): object;
}
class Foo implements iFoo {
public function bar(string $baz): self {...}
}
它从 PHP 7.4 开始工作。
PHP 8 将添加“静态返回类型”,这将解决您的问题。
查看此 RFC: https : //wiki.php.net/rfc/static_return_type
使用PHP 7.3运行一些测试,当我这样做时,我不能让它抱怨(即使是严格的)...
interface A {
function f() {}
}
interface B {
function f():self {}
}
要么我的测试破坏了,要么PHP已经改变了一些东西。 一般来说,如果减少可能的返回类型,那么往往不会破坏OOP。 在任何使用该方法的类中都可以处理任何返回,包括返回类型的子集。 相反的情况与参数大致相同。
他们在7.2中实现了这一点。
这对我来说似乎是预期的行为。
只需更改您的Foo::bar
方法以返回iFoo
而不是self
并完成它。
说明:
接口中使用的self
表示“类型为iFoo
的对象”。
self
中使用的self
表示“类型为Foo
的对象”。
因此,接口和实现中的返回类型显然是不一样的。
其中一条评论提到了 Java 以及您是否会遇到此问题。 答案是肯定的,如果 Java 允许您编写这样的代码,您会遇到同样的问题——但事实并非如此。 由于 Java 要求您使用类型的名称而不是 PHP 的self
快捷方式,因此您永远不会真正看到这一点。 (有关 Java 中类似问题的讨论,请参见此处。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.