細說PHP 7.2 子類別覆蓋方法省略參數類型功能以及Liskov 替換原則
PHP 7.2 出來也有一段時間了,關於新版本有什麼新改進,只要你關心PHP 的發展,應該都看過。這裡只細說一個可能會有誤解的新功能。
PHP 7.2 可以在當子類別覆寫(override)父類別方法的時候,忽略父類別方法的定義的參數的類型(type hint):
class Foo { public function bar(SomeClass $obj) {} } class Foobar extends Foo { public function bar($obj) {} // 这在 PHP7.2 版本之前是会报错的 }
我看有些網站介紹此功能的時候,說其目的是為了『方便重構。如果以後父類別方法的參數型別變了,子類別不用再全部換一遍』。聽起來好像很有道理。依照這個說法,隱含的意思是:如果子類別忽略了父類別方法參數類型,被呼叫時還是會檢查參數類型。實際情況是不是這樣做一下實驗就知道了:
<?php class Foo { } class Bar { public function setFoo(Foo $foo) { } } class BarKid extends Bar { public function setFoo($foo) { } } $kid = new BarKid; $kid->setFoo('I am a string!');
如果上面的說法是對的,setFoo 接受字符串參數的時候就應該報錯,然而上面代碼在7.2 下並沒有任何報錯信息,但如果子類別的setFoo 方法加上了參數類型,就會立刻報錯了。記住網路上很多說法都不可信,除了我這個小站…
上面的實驗說明子類別方法可省略參數類型,其目的肯定不是為了方便重構。那真正目的是什麼呢?
在 PHP 7.1 裡有一個新功能,是『可設定方法或函數的參數和回傳類型是否可以為 null』。其中有一條看上去比較彆扭的規則:『子類別方法參數型別範圍放寬(即父類別參數若不能為null ,子類別參數可支援null),但傳回型別縮(父類別若不能傳回null,子類別必須也不行;若父類別可以回傳null,子類別可以不回傳null)’,當時我很簡單說了一句,是因為『Liskov 替換原則』,但沒有做深入介紹。身邊的 PHPer 關注 OOP 原則的不多,但我認為它應該被每個工程師知道,還是介紹一下。
Liskov 替換原則簡單一句話:父類別出現的地方,替換成子類別也能運行,即子類別可無腦替換父類別。 其實從語言設計來說,我認為此原則就是對自然規則的模仿2018-09-29 補充:也不是簡單的『模仿』,有興趣可閱讀新部落格『企鵝不是鳥’。
舉個例子,人可以喝酒,喝茶,喝可樂,喝各種飲料,但人作為哺乳動物,怎麼著都能喝水吧?但反過來,哺乳類動物能喝水,但不一定能喝酒喝茶喝可樂,所以人是哺乳類動物的子類。
從語言設計的角度來說,子類別就應該是父類別的加強版,就是要能比父類別處理更多的物件類型,而被覆寫的方法參數類型的擴大,也是這一原則的體現。
再來來說可能有點繞的回傳類型,為什麼子類別要縮小回傳的範圍呢?其實只要假設一個方法的返回會作為另一個方法的參數,就很好想了。例如一個『水果飲料廠』類,有一個『生產』方法,返回『水果汁』,並傳給了『小朋友』的『喝』方法。有一個『橘子汁工廠』類屬於『水果飲料廠』的子類,它的『生產』方法返回類型縮緊,只能返回『橘子汁』,依然給『小朋友』『喝』,並不會出現任何問題。
再舉一個反例。如果又出現一個『水果飲料廠』的子類,其『生產』方法除了返回水果汁,還能返回果釀酒,那這個子類很顯然不能冒著給小朋友喝酒的風險去替換父類。
說完了 Liskov 替換原則,我們再來看看 7.2 裡的這個改進,我們這時應該知道其實這也是 Liskov 原則的體現。目前來說,替換原則在 PHP 的實作並不完全。可能有人覺得這個版本是不是也支援『父類別沒有回傳類型,子類別可以有回傳類型』呢?可惜的是至少在 7.2 這個版本,不支持,大家可以自行實驗一下。
7.2 的另一個新功能,是 object 可以作為任何物件的類型。請參閱官方提供範例:
<?php function test(object $obj) : object { return new SplQueue(); } test(new StdClass());
其實在7.2 發布之前,也是出於替換原則,有過一次關於『是否子類別可以用object 類型來替代被覆蓋的方法物件參數的類型』,但最終投票並沒有通過。雖然我不知道原因,但起碼有人提了。
另外目前PHP 不能像Java 那樣重載(overload),沒有辦法可以指定覆蓋的方法的類型(目前只能把類型直接去掉,有點太粗暴):
<?php class Foo { } class FooFoo extends Foo { } class Bar { public function foo(FooFoo $foo) { } } class BarBar extends Bar { public function foo(Foo $foo) // 依然会报『子类不兼容父类方法格式』的错误 { } }
但PHP 也在不斷的改變不是嗎?最近 PHP 版本迭代這麼快,我對 PHP 成為一個支援更多 OOP 特性的語言還是非常有信心的!
推薦學習:《PHP7教學》
以上是分析PHP7.2忽略父類別方法以及Liskov替換原則相關問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Linux新版
SublimeText3 Linux最新版

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

WebStorm Mac版
好用的JavaScript開發工具