scala 學習: 逆變和協變


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>

  

同理如返回值。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM