[Scala] 了解 协变 与 逆变

首先定义一个类 A,其参数类型 T 为协变,类中包含一个方法 func,该方法有一个类型为 T 的参数:html

  1 class A[+T] {
  2   def func(x: T) {}
  3 }

此时在 x 处会有异常提示,covariant type T occurs in contravariant position in type T of value xweb

如今,先 假设该类定义编译能够经过
由于 String→AnyRef(String 是 AnyRef 的子类),参数类型 T 为协变,因此 A[String]→A[AnyRef](A[String] 也是 A[AnyRef] 的子类)。
定义两个对象以下:
val father: A[AnyRef] = null.asInstanceOf[A[AnyRef]] // 父类对象,包含方法 func(x: AnyRef)
val child: A[String] = null.asInstanceOf[A[String]] // 子类对象,包含方法 func(x: String)
根据 里氏替换原则,调用 father.func(Nil) 里的 father 对象应该能够直接替换成 child 对象,可是 child.func(Nil) 调用显然异常。
或者定义以下一个函数,接收 A[AnyRef] 做为参数:
  1 def otherFunc(x: A[AnyRef]): Unit = {
  2   x.func(Nil)
  3 }
同理调用 otherFunc(father) 里的 father 对象也应该能够直接替换成 child 对象,可是如此一来 otherFunc 中的参数要调用 func 方法就只能传入 String 类型的参数了(由于 child 对象中的 func 方法只能接收 String 类型参数),至关于 otherFunc 的处理范围缩小了,这是不容许的。
也就是说上面 A 类的定义违反了里氏替换原则,因此编译器没法经过。
图解上述分析过程:
e8cdf7ab-5d87-4a66-8a2e-07e63f90899d[4]
反之,若是用 father 的父类对象替换,至关于 otherFunc 的处理范围变大了,此时 otherFunc 中的参数要调用 func 方法,完成能够传入 AnyRef 类型的参数。
  1 val fatherFather: A[Any] = null.asInstanceOf[A[Any]] // func(x: Any)
  2 otherFunc(fatherFather)// 若是 T 为协变,此处会提示异常

总结:

协变点(covariant position)方法返回值的位置;
逆变点(contravariant position)方法参数的位置;
由于 A 中的类型参数 T 声明为协变,而 T 又是 func 方法中的参数类型,属于逆变点,因此此时编译器会提示异常:
covariant type T occurs in contravariant position in type T of value x
 
 
  • 若是将 T 做为 func 方法的返回值类型,即处于协变点,就能够编译经过。
        此时,回到以前的 otherFunc(father) 和 otherFunc(child),child 替换 father 后,child 调用 func 返回 String 类型的对象替换 AnyRef 是合理的。
  1 class B[+T] {
  2   def func(): T = {
  3     null.asInstanceOf[T]
  4   }
  5 }
  6 // 与 Function0[+A] 等价
  • 或者将类型参数 T 改成逆变,
  1 class A[-T] {
  2   def func(x: T) {}
  3 }
  4 // 与 Function1[-A, Unit] 等价
  5 
  6 val father: A[AnyRef] = null.asInstanceOf[A[AnyRef]] // func(x: AnyRef)
  7 val fatherFather: A[Any] = null.asInstanceOf[A[Any]] // func(x: Any)
  8 
  9 def otherFunc(x: A[AnyRef]): Unit = {
 10   x.func(Nil)
 11 }
 12 
 13 otherFunc(father) // success
 14 otherFunc(fatherFather)// success
 

参考网址:

 
by. Memento
相关文章
相关标签/搜索