簡體   English   中英

PHP 7個接口,返回類型提示和自我

[英]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這樣的說法

繼承期間聲明的返回類型的執行是不變的; 這意味着當子類型覆蓋父方法時,子類型的返回類型必須與父方法完全匹配並且不能省略。 如果父級未聲明返回類型,則允許子級聲明返回類型。

...

該 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM