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
  • 相关阅读:
    SQL2012 创建备份计划
    rem 移动端适配
    sql server 表结构 导出 到excel
    针对OAuth2的CSRF攻击
    xss、SQL测试用例小结
    svn报错:[Previous operation has not finished; run 'cleanup' if it was interrupted] 的排错过程
    并发数与在线用户之间的关系
    loadrunner--常用函数列表【转】
    LR参数化类型为file显示大于100数据方法
    loadrunner11--集合点(Rendezvous )菜单是灰色不能点击
  • 原文地址:https://www.cnblogs.com/memento/p/8655770.html
Copyright © 2011-2022 走看看