逆变
- 符号将类型参数标记为逆变 - 这里我们说“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] = ???
}