細說 PHP 7.2 子類覆蓋方法省略參數(shù)類型功能以及 Liskov 替換原則
PHP 7.2 出來也有段時間了,關(guān)于新版本有什么新改進,只要你關(guān)心 PHP 的發(fā)展,應該都看過。這里只細說一個可能會有誤解的新功能。
PHP 7.2 可以在當子類覆蓋(override)父類方法的時候,忽略父類方法的定義的參數(shù)的類型(type hint):
class Foo { public function bar(SomeClass $obj) {} } class Foobar extends Foo { public function bar($obj) {} // 這在 PHP7.2 版本之前是會報錯的 }
我看有些網(wǎng)站介紹此功能的時候,說其目的是為了『方便重構(gòu)。如果以后父類方法的參數(shù)類型變了,子類不用再全部換一遍』。聽起來好像很有道理。按這說法,隱含的意思是:如果子類忽略了父類方法參數(shù)類型,被調(diào)用時還是會檢查參數(shù)類型。實際情況是不是這樣做一下實驗就知道了:
<?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 接受字符串參數(shù)的時候就應該報錯,然而上面代碼在 7.2 下并沒有任何報錯信息,但如果子類的 setFoo 方法加上了參數(shù)類型,就會立馬報錯了。記住網(wǎng)上很多說法都不可信,除了我這個小站……
上面的實驗說明子類方法可省略參數(shù)類型,其目的肯定不是為了方便重構(gòu)。那真正目的是什么呢?
在 PHP 7.1 里有一個新功能,是『可設(shè)置方法或函數(shù)的參數(shù)和返回類型是否可以為 null』。其中有一條看上去比較別扭的規(guī)則:『子類方法參數(shù)類型范圍放寬(即父類參數(shù)若不能為 null ,子類參數(shù)可支持 null),但返回類型縮緊(父類若不能返回 null,子類必須也不行;若父類可以返回 null,子類可以不返回 null)』,當時我很簡單說了一句,是因為 『Liskov 替換原則』,但沒有做深入介紹。身邊的 PHPer 們關(guān)注 OOP 原則的不多,但我認為它應該被每個工程師知道,還是介紹一下。
Liskov 替換原則簡單一句話:父類出現(xiàn)的地方,替換成子類也能運行,即子類可無腦替換父類。其實從語言設(shè)計來說,我認為此原則就是對自然規(guī)則的模仿2018-09-29 補充:也不是簡單的『模仿』,有興趣可閱讀新博客『企鵝不是鳥』。
舉個例子,人可以喝酒,喝茶,喝可樂,喝各種飲料,但人作為哺乳動物,怎么著都能喝水吧?但反過來,哺乳動物能喝水,但不一定能喝酒喝茶喝可樂,所以人是哺乳動物的子類。
從語言設(shè)計的角度來說,子類就應該是父類的加強版,就是要能比父類處理