溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么理解PHP7.2忽略父類方法以及Liskov替換原則相關(guān)問(wèn)題

發(fā)布時(shí)間:2021-11-16 15:23:06 來(lái)源:億速云 閱讀:108 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“怎么理解PHP7.2忽略父類方法以及Liskov替換原則相關(guān)問(wèn)題”,在日常操作中,相信很多人在怎么理解PHP7.2忽略父類方法以及Liskov替換原則相關(guān)問(wèn)題問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么理解PHP7.2忽略父類方法以及Liskov替換原則相關(guān)問(wèn)題”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

細(xì)說(shuō) PHP 7.2 子類覆蓋方法省略參數(shù)類型功能以及 Liskov 替換原則

PHP 7.2 出來(lái)也有段時(shí)間了,關(guān)于新版本有什么新改進(jìn),只要你關(guān)心 PHP 的發(fā)展,應(yīng)該都看過(guò)。這里只細(xì)說(shuō)一個(gè)可能會(huì)有誤解的新功能。

PHP 7.2 可以在當(dāng)子類覆蓋(override)父類方法的時(shí)候,忽略父類方法的定義的參數(shù)的類型(type hint):

class Foo
{
    public function bar(SomeClass $obj) {}
}
class Foobar extends Foo
{
    public function bar($obj) {} // 這在 PHP7.2 版本之前是會(huì)報(bào)錯(cuò)的
}

我看有些網(wǎng)站介紹此功能的時(shí)候,說(shuō)其目的是為了『方便重構(gòu)。如果以后父類方法的參數(shù)類型變了,子類不用再全部換一遍』。聽起來(lái)好像很有道理。按這說(shuō)法,隱含的意思是:如果子類忽略了父類方法參數(shù)類型,被調(diào)用時(shí)還是會(huì)檢查參數(shù)類型。實(shí)際情況是不是這樣做一下實(shí)驗(yàn)就知道了:

<?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!');

如果上面的說(shuō)法是對(duì)的,setFoo 接受字符串參數(shù)的時(shí)候就應(yīng)該報(bào)錯(cuò),然而上面代碼在 7.2 下并沒有任何報(bào)錯(cuò)信息,但如果子類的 setFoo 方法加上了參數(shù)類型,就會(huì)立馬報(bào)錯(cuò)了。記住網(wǎng)上很多說(shuō)法都不可信,除了我這個(gè)小站……

上面的實(shí)驗(yàn)說(shuō)明子類方法可省略參數(shù)類型,其目的肯定不是為了方便重構(gòu)。那真正目的是什么呢?

在 PHP 7.1 里有一個(gè)新功能,是『可設(shè)置方法或函數(shù)的參數(shù)和返回類型是否可以為 null』。其中有一條看上去比較別扭的規(guī)則:『子類方法參數(shù)類型范圍放寬(即父類參數(shù)若不能為 null ,子類參數(shù)可支持 null),但返回類型縮緊(父類若不能返回 null,子類必須也不行;若父類可以返回 null,子類可以不返回 null)』,當(dāng)時(shí)我很簡(jiǎn)單說(shuō)了一句,是因?yàn)?『Liskov 替換原則』,但沒有做深入介紹。身邊的 PHPer 們關(guān)注 OOP 原則的不多,但我認(rèn)為它應(yīng)該被每個(gè)工程師知道,還是介紹一下。

Liskov 替換原則簡(jiǎn)單一句話:父類出現(xiàn)的地方,替換成子類也能運(yùn)行,即子類可無(wú)腦替換父類。其實(shí)從語(yǔ)言設(shè)計(jì)來(lái)說(shuō),我認(rèn)為此原則就是對(duì)自然規(guī)則的模仿2018-09-29 補(bǔ)充:也不是簡(jiǎn)單的『模仿』,有興趣可閱讀新博客『企鵝不是鳥』。

舉個(gè)例子,人可以喝酒,喝茶,喝可樂,喝各種飲料,但人作為哺乳動(dòng)物,怎么著都能喝水吧?但反過(guò)來(lái),哺乳動(dòng)物能喝水,但不一定能喝酒喝茶喝可樂,所以人是哺乳動(dòng)物的子類。

從語(yǔ)言設(shè)計(jì)的角度來(lái)說(shuō),子類就應(yīng)該是父類的加強(qiáng)版,就是要能比父類處理更多的對(duì)象類型,而被覆寫的方法參數(shù)類型的擴(kuò)大,也是這一原則的體現(xiàn)。

再來(lái)說(shuō)可能有點(diǎn)繞的返回類型,為什么子類要縮小返回的范圍呢?其實(shí)只要假設(shè)一個(gè)方法的返回會(huì)作為另外一個(gè)方法的參數(shù),就很好想了。比如一個(gè)『水果飲料廠』類,有一個(gè)『生產(chǎn)』方法,返回『水果汁』,并傳給了『小朋友』的『喝』方法。有一個(gè)『橘子汁工廠』類屬于『水果飲料廠』的子類,它的『生產(chǎn)』方法返回類型縮緊,只能返回『橘子汁』,依然給『小朋友』『喝』,并不會(huì)出現(xiàn)任何問(wèn)題。

再舉一個(gè)反例。如果又出現(xiàn)一個(gè)『水果飲料廠』的子類,其『生產(chǎn)』方法除了返回水果汁,還能返回果釀酒,那這個(gè)子類很顯然不能冒著給小朋友喝酒的風(fēng)險(xiǎn)去替換父類。

說(shuō)完了 Liskov 替換原則,我們?cè)賮?lái)看看 7.2 里的這個(gè)改進(jìn),我們這時(shí)應(yīng)該知道其實(shí)這也是 Liskov 原則的體現(xiàn)。目前來(lái)說(shuō),替換原則在 PHP 的實(shí)現(xiàn)并不完全。可能有人覺得這個(gè)版本是不是也支持『父類沒有返回類型,子類可以有返回類型』呢?遺憾的是至少在 7.2 這個(gè)版本,并不支持,大家可以自行實(shí)驗(yàn)一下。

7.2 的另外一個(gè)新功能,是 object 可以作為任何對(duì)象的類型。見官方提供例子:

<?php
function test(object $obj) : object
{
    return new SplQueue();
}
test(new StdClass());

其實(shí)在 7.2 發(fā)布之前,也是出于替換原則,有過(guò)一次關(guān)于『是否子類可以用 object 類型來(lái)替代被覆蓋的方法對(duì)象參數(shù)的類型』,但最終投票并沒有通過(guò)。雖然我不知道原因,但起碼有人提了。

另外目前 PHP 不能像 Java 那樣重載(overload),沒有辦法可以指定覆蓋的方法的類型(目前只能把類型直接去掉,有點(diǎn)太粗暴):

<?php
class Foo
{
}
class FooFoo extends Foo
{
}
class Bar
{
    public function foo(FooFoo $foo)
    {
    }
}
class BarBar extends Bar
{
    public function foo(Foo $foo) // 依然會(huì)報(bào)『子類不兼容父類方法格式』的錯(cuò)誤
    {
    }
}

到此,關(guān)于“怎么理解PHP7.2忽略父類方法以及Liskov替換原則相關(guān)問(wèn)題”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

php
AI