我想解釋一下為什麼共變數和逆變數很重要,以及為什麼它們分別應用於返回類型和參數類型,而不是相反。
共變數可能最容易理解,並且與里氏替換原則直接相關。使用上面的例子,假設我們收到一個 `AnimalShelter` 物件,然後我們想要透過呼叫它的 `adopt()` 方法來使用它。我們知道它返回一個 `Animal` 物件,無論該物件究竟是什麼,例如它是 `Cat` 還是 `Dog`,我們都可以將它們視為相同。因此,特化返回類型是可以的:我們至少知道任何可以返回的東西的共同介面,並且我們可以以相同的方式處理所有這些值。
逆變 (Contravariance) 的概念稍微複雜一點。它與提升方法靈活性的實用性息息相關。再次以先前的例子來說,基礎方法 `eat()` 接受特定類型的食物;然而,某個特定動物可能想要支援更廣泛的食物類型。或許就像先前的例子一樣,它在原始方法中添加了允許其食用任何種類食物的功能,而不僅限於動物專用的食物。「動物」中的基礎方法已經實現了允許其食用動物專用食物的功能。「狗」類別中的覆寫方法可以檢查參數是否屬於 `AnimalFood` 類型,如果是,則只需呼叫 `parent::eat($food)`。如果參數不屬於特定類型,它可以對該參數執行額外或甚至完全不同的處理 - 而不會破壞原始簽章,因為它仍然處理特定類型,但也處理更多類型。這就是為什麼它也與里氏替換原則 (Liskov Substitution Principle) 密切相關:消費者仍然可以將特定食物類型傳遞給「動物」,而無需確切知道它是「貓」還是「狗」。