簡體   English   中英

如果我們已經有類型邊界,為什么我們需要變化?

[英]Why do we need variance if we already have type boundaries?

如果我寫Foo[_ <: Bar]Foo[+T <: Bar]后者讓我做什么,我不能用前者做什么?

它只是方便,所以可以寫def bar: T而不是def bar: Bar

它在哪種情況下有用?

是否真的准確地說java中沒有變化? 可以不用<? extends Foo> <? extends Foo><? super Bar> <? super Bar>

類型邊界控制哪些類型是有效參數。 協方差/逆變量注釋控制具有不同參數的實例之間的子/超類型關系。 Foo[+T]表示Foo[A]是一種亞型Foo[B]如果A是的子類型B Foo[-T]意味着相反。

class Foo1[T <: Bar]()
class Foo2[+T <: Bar]()
trait Bar
trait Baz extends Bar

val f1: Foo1[Bar] = new Foo1[Baz]() // compile error
val f2: Foo2[Bar] = new Foo2[Baz]() // works just fine

與使用Foo1[_ <: Bar]等類型邊界相反的一個好處是編譯器將在類本身上強制執行某些屬性。 例如,這不會編譯:

class Foo[+T]() {
  def f(t: T): Unit = {}
}

這也不會:

class Foo[-T]() {
  def f(): T = { ??? }
}

據我所知,Java沒有辦法明確表示協方差或逆變。 這導致了很多錯誤,尤其是數組是隱式協變的,即使它們不應該是因為它們是可變的。

是否真的准確地說java中沒有變化? 可以不用<? extends Foo> <? extends Foo><? super Bar> <? super Bar>

通常認為Java具有使用站點差異,而不是Scala聲明站點差異(嗯,實際上Scala支持兩者)。 它實際上更具表現力,嚴格來說:你可以編寫更明智的程序,例如,不將任何內容放入List方法可能是協變的,而放置但不看內容的方法可能是逆變的。 使用聲明站點差異,您需要具有單獨的不可變和可變類型。

這個問題的一個眾所周知的症狀是Scala中Set#contains的簽名,它不能只接受A而不強制Set不變。

只有使用站點方差的問題在於,如果你想要保持一致,它會使使用該類型的所有方法的簽名變得復雜:不僅僅是在類型本身上聲明的那些,而是那些調用它們的方法。

另請參閱Java的使用站點差異與C#的聲明站點差異相比如何? https://kotlinlang.org/docs/reference/generics.html#variance

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM