2017-07-02 82 views
3

我正在學習函數式編程,我試圖理解協方差和逆變概念。我現在的問題是:我不知道什麼時候應該對協變類型應用協變和逆變。在具體的例子中,是的,我可以確定。但是在一般情況下,我不知道哪個通用規則。什麼時候應該使用協變和反變換當設計一個類與泛型

例如這裏是我研究的一些規則:

  • 如果泛型類型作爲參數:使用逆變。 (1)
  • 如果泛型類型用作逆向值:使用協方差。 (2)

在我研究這個概念時所瞭解的一些語言也使用這些約定。例如:in關鍵字的協方差(在斯卡拉是+)和關鍵字的反例(斯卡拉是 - )。要點(1)容易理解。但在點(2),我看到異常:

  • methodA(Iterator<A>) A應該是協方差
  • methodA(Comparator<A>) A應該是逆變。

所以這裏的例外是:雖然這兩種情況使用泛型類型作爲輸入,但一個應該是協方差和其他應該是逆變。我的問題是:在設計一個班級時,我們是否有任何一般規則來決定協方差/逆變?

感謝

+0

如果一個參數在共同和逆變位置,那麼它必須是不變的。這最終適用於你的兩個例子 - 它們都是不變的。 – Dima

+0

我知道如果協變和逆變都應該是不變的。我的例子最後是兩個不同的例子。 –

回答

2

協方差和逆變像數的算術跡象,當你窩在一個又一個與方差的位置,組成不同。

比較:

1] +(+a) = +a 
2] -(+a) = -a 
3] +(-a) = -a 
4] -(-a) = +a 

trait +[+A] { def make(): A } // Produces an A 
trait -[-A] { def break(a: A) } // Consumes an A 

1] 
    // Produces an A after one indirection: x.makeMake().make() 
    trait ++[+A] { def makeMake(): +[A] } 
    +[+[A]] = +[A] 
2] 
    // Consumes an A through one indirection: x.breakMake(new +[A] { override def make() = a }) 
    trait -+[-A] { def breakMake(m: +[A]) } 
    -[+[A]] = -[A] 
3] 
    // Consumes an A after one indirection: x.makeBreak().break(a) 
    trait +-[-A] { def makeBreak(): -[A] } 
    +[-[A]] = -[A] 
4] 
    // Produces an A through one indirection 
    // Slightly harder to see than the others 
    // x.breakBreak(new -[A] { override def break(a: A) = { 
    // you have access to an A here, so it's like it produced an A for you 
    // }}) 
    trait --[+A] { def breakBreak(b: -[A]) } 
    -[-[A]] = +[A] 

所以,當你有

def method(iter: Iterator[A]) 

方法的參數作爲一個整體處於逆變位置,A處於Iterator內部的協變位置,但是-[+[A]] = -[A],所以A實際上是在這個簽名裏面的一個逆變位置,並且類需要說-A。這是有道理的,外部用戶傳遞了一堆A,所以它應該是逆變的。

同樣,在

def method(comp: Comparator[A]) 

整個方法的參數是在逆變位置,A是在Comparator內的逆變位置,讓您撰寫他們-[-[A]] = +[A],你看到A真是一個協變位置。這也是有道理的。當您將A傳遞到Comparator時,外部用戶可以控制它的功能,因此有點像將A返回給它們。

+0

https://stackoverflow.com/questions/2501023/demonstrate-covariance-and-contravariance-in-java 作爲第二個答案告訴我們與你相反。你的答案和第二個答案在這裏有什麼不同嗎? –

+0

更重要的是,正如@迪瑪所言,我認爲如果一種類型可以是逆變和協變,那麼這種類型應該是不變的嗎? –

+0

問題在於範圍。在那個答案中,*在Iterable中,T應該是協變的,而在* class *之外是* outside,因此'T'必須是逆變的。你的問題是關於創建類,所以我在我的答案中使用了這個範圍。這個問題是關於Java,它不能做差異,所以它們被迫使用通配符在參數級別上進行模擬。 – HTNW

相關問題