逆變
-
符號將型別引數標記為逆變 - 這裡我們說“Handler
在 A
上是逆變的”:
trait Handler[-A] {
def handle(a: A): Unit
}
逆變型引數可以被認為是輸入型別。將 A
標記為逆變斷言 Handler[X] <: Handler[Y]
提供了 X >: Y
。例如,Handler[Animal]
是有效的 Handler[Cat]
,因為 Handler[Animal]
也必須處理貓。
逆變型別引數不能出現在協變(輸出)位置。下面的例子將不會編譯,因為我們斷言 Contra[Animal] <: Contra[Cat]
,但是 Contra[Animal]
有 def produce: Animal
,不能保證按照 Contra[Cat]
的要求生產貓!
trait Contra[-A] {
def handle(a: A): Unit
def produce: A
}
但是要注意:為了超載解析度,逆變也會違反直覺反轉型別對逆變型別引數的特異性 - Handler[Animal]
被認為比 Handler[Cat]
更具體。
由於無法在型別引數上過載方法,因此在解析隱式引數時,此行為通常只會出現問題。在以下示例中,將永遠不會使用 ofCat
,因為 ofAnimal
的返回型別更具體:
implicit def ofAnimal: Handler[Animal] = ???
implicit def ofCat: Handler[Cat] = ???
implicitly[Handler[Cat]].handle(new Cat)
此行為目前預定在 dotty 中更改 ,這就是為什麼(作為示例)scala.math.Ordering
對其型別引數 T
不變的原因。一種解決方法是使型別類不變,並在希望它應用於給定型別的子類的事件中對隱式定義進行型別引數化:
trait Person
object Person {
implicit def ordering[A <: Person]: Ordering[A] = ???
}