簡體   English   中英

參數類型可以在 PHP 中專門化嗎

[英]Can parameter types be specialized in PHP

假設我們有以下兩個類:

abstract class Foo {
    public abstract function run(TypeA $object);
}

class Bar extends Foo {
    public function run(TypeB $object) {
        // Some code here
    }
}

TypeB 類擴展了 TypeA 類。

嘗試使用它會產生以下錯誤消息:

Bar::run() 的聲明必須與 Foo::run() 的聲明兼容

當涉及到參數類型時,PHP 真的就這么壞了嗎,還是我只是錯過了這一點?


這個答案自PHP 7.4 (部分自 7.2) 起已過時。


您描述的行為稱為協方差,在 PHP 中根本不受支持。 我不知道內部原理,但我可能懷疑 PHP 的核心在應用所謂的“類型提示”檢查時根本不評估繼承樹。

順便說一句,PHP 也不支持這些類型提示的逆變(其他 OOP 語言中通常支持的功能) - 最有可能的原因是上面懷疑的。 所以這也不起作用:

abstract class Foo {
    public abstract function run(TypeB $object);
}

class Bar extends Foo {
    public function run(TypeA $object) {
        // Some code here
    }
}

最后還有更多信息: http : //www.php.net/~derick/meeting-notes.html#implement-inheritance-rules-for-type-hints

這似乎與大多數面向對象的原則非常一致。 PHP 不像 .Net - 它不允許您覆蓋類成員。 Foo任何擴展都應該滑入之前使用Foo地方,這意味着您不能放松約束。

簡單的解決方案顯然是去除類型約束,但如果Bar::run()需要不同的參數類型,那么它實際上是一個不同的函數,理想情況下應該有一個不同的名稱。

如果TypeATypeB有任何共同點,請將共同元素移動到基類並將其用作參數約束。

我認為這是設計使然:定義其方法的底層行為是抽象定義的重點。

從抽象類繼承時,父類聲明中所有標記為抽象的方法必須由子類定義; 此外,這些方法必須定義為具有相同(或較少限制)的可見性。

人們總是可以在代碼中添加約束:

public function run(TypeA $object) {
    assert( is_a($object, "TypeB") );

您必須記住或記錄特定的類型限制。 優點是它成為純粹的開發工具,因為斷言通常在生產服務器上關閉。 (實際上,這是在開發過程中發現的一類錯誤,而不是隨機中斷生產。)

問題中顯示的代碼不會在 PHP 中編譯。 如果確實如此,則Bar類將無法兌現其父級Foo做出的能夠接受TypeA任何實例的保證,並違反Liskov 替換原則

目前相反的代碼也不會編譯,但在 PHP 7.4 版本中,預計在 2019 年 11 月 28 日發布,下面的類似代碼有效,使用新的逆變參數功能:

abstract class Foo {
    public abstract function run(TypeB $object); // TypeB extends TypeA
}

class Bar extends Foo {
    public function run(TypeA $object) {
        // Some code here
    }
}

所有的 Bars 都是 Foos,但並非所有的 Foos 都是 Bars。 所有 TypeB 都是 TypeAs,但並非所有 TypeAs 都是 TypeB。 任何 Foo 都可以接受任何 TypeB。 那些也是 Bars 的 Foos 也將能夠接受非 TypeB TypeAs。

PHP 還將支持協變返回類型,它們以相反的方式工作。

盡管您不能使用整個類層次結構作為類型提示。 可以使用selfparent關鍵字在某些情況下強制執行類似的操作。

從 PHP 手冊注釋中引用r dot wilczek 在 web-appz dot de處:

<?php
interface Foo 
{
    public function baz(self $object);
}

class Bar implements Foo
{
    public function baz(self $object)
    {
        // 
    }
}
?>

現在還沒有提到的是,您也可以使用 'parent' 作為類型提示。 帶有接口的示例:

<?php
interface Foo 
{
    public function baz(parent $object); 
}

class Baz {}
class Bar extends Baz implements Foo
{
    public function baz(parent $object)
    {
        // 
    }
}
?>

Bar::baz() 現在將接受 Baz 的任何實例。 如果 Bar 不是任何類的繼承人(沒有“擴展”),PHP 將引發致命錯誤:“當當前類作用域沒有父類時,無法訪問父類::”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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