zoukankan      html  css  js  c++  java
  • [Scala] 了解 协变 与 逆变

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

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

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

    现在,先假设该类定义编译可以通过
    因为 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
  • 相关阅读:
    poj 2996 模拟
    poj 2965 BFS
    poj 1068 模拟
    poj 3295 前缀表达式求值
    常用的十七大学术搜索引擎
    Why,Unix or Linux ?
    匈牙利命名法
    微调控件(CSpinButtonCtrl)
    美国免费邮箱
    ASP常用的代码
  • 原文地址:https://www.cnblogs.com/memento/p/8655770.html
Copyright © 2011-2022 走看看