scala 逆變和協變的概念網上有很多解釋, 總結一句話就是
參數是逆變的或者不變的,返回值是協變的或者不變的。
但是為什么是這樣的?
協變:
當s 是A的子類, 那么func(s) 是func(A)的子類。 也就是被參數化類型的泛化方向與參數類型的方向是一致的,所以稱為協變。
個人理解的func(s) 是func(A)的子類的意思是: func(s)的返回值是func(A)的返回值的子類。
逆變:
同協變定義,但是是反過來,即當S是A的子類時,func(S)是func(A)的父類。
如下圖所示:
為什么函數的參數是逆變 而函數的返回值是協變呢?
根據上面的解釋: 入參是用來消費的, 如:animal > bird > dodo
假設有一個函數用來輸出鳥類的叫聲,func[-T], 這時 函數可以接受的參數為bird 和animal, 是合理的。
如果定義為func[+T], 函數接受的參數為 bird 和dodo, 對於參數為dodo類型來說,傳另一個鳥如燕子, 則會出錯。因此不符合要求。
scala> class Animal { val sound = "rustle" } defined class Animal scala> class Bird extends Animal { override val sound = "call" } defined class Bird scala> class Chicken extends Bird { override val sound = "cluck" } defined class Chicken 假設你需要一個以Bird為參數的函數: scala> val getTweet: (Bird => String) = // TODO 標准動物庫有一個函數滿足了你的需求,但它的參數是Animal。在大多數情況下,如果你說“我需要一個___,我有一個___的子類”是可以的。
但是,在函數參數這里是逆變的。如果你需要一個接受參數類型Bird的函數變量,但卻將這個變量指向了接受參數類型為Chicken的函數,
那么給它傳入一個Duck時就會出錯。然而,如果將該變量指向一個接受參數類型為Animal的函數就不會有這種問題: scala> val getTweet: (Bird => String) = ((a: Animal) => a.sound ) getTweet: Bird => String = <function1>
同理如返回值。