[英]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.