[Scala的协变和逆变]

    Scala中协变和逆变主要作用是用来解决参数化类型的泛化问题。由于参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数是继承关系(可泛化),那被参数化的类型是否也可以泛化呢?在Java中这种情况下是不可泛化的,然而Scala提供了三个选择,即协变、逆变和非变,解决了参数化类型的泛化问题。

协变和逆变

     Scala语言中,协变和逆变到处可见。如List,Queue等属于协变协变和逆变的一种。

   协变和逆变使用“+,-”差异标记。当我们定义一个协变类型List[+A]时,List[Child]可以是List[Parent]的子类型,当我们定义一个逆变类型List[-A]时,List[Child]可以是List[Parent]的父类型

+BB的超集,叫协变

-AA的子集,叫逆变

假设有参数化特质List,那么可以有三种定义。如下所示:

(1)  trait List [T]{} 

非变这种情况下,当类型S是类型A的子类型,则List [S]不可以认为是List [A]的子类型或父类型,这种情况和Java是一样的。 

(2)  trait List [+T]{} 

             协变。如果Sextends A (S为子类型,A为父类型)List [S]为子类型,List [A]为父类型S <: A => List [S] <: List [A]

(3)  trait List [-T]{} 

逆变。如果S extends A (S为子类型,A为父类型)List [S]为父类型,List [A]为子类型,和协变互逆S <: A => Queue[S] >: Queue[A]

    那么,在Scala中如何定义协变逆变类呢,举例如下。

objectCovariantAndContravariantDemo {
  defmain(args: Array[String]): Unit = {
    //不变
    definv1: Invariant[Tiger] = newInvariant[Tiger]()
    //不可以赋值,编译器编译不通过
    definv2: Invariant[Cat] = inv1
 
    //协变
    defcov1: Covariant[Tiger] = newCovariant[Tiger]()
    //可以直接赋值
    defcov2: Covariant[Cat] = cov1
 
    //逆变
    defcont1: Contravariant[Cat] = newContravariant[Cat]()
    //可以直接赋值
    defcont2: Contravariant[Tiger] = cont1
  }

  /**
   * 定义一个不可变的类
   */
  class Invariant[T] {}

  /**
   * 定义一个协变的类
   */
  class Covariant[+T] {}

  /**
   * 定义一个逆变的类
   */
  class Contravariant[-T] {}

  /**
   * 定义一个动物的类,具有行为(吃)
   */
  class Animal {
    def eat() {
      println("Animal likeeat botany.")
    }
  }
 
  /**
   * 定义动物(猫)的类,具有行为(吃)
   */
  class Cat extends Animal {
    override def eat() {
      println("Cat eatfish.")
    }
  }

  /**
   * 定义动物(老虎)的类,具有行为(吃)
   */
  class Tiger extends Cat {
    override def eat() {
      println("Tiger eatmeat.")
    }
  }
}

上界和下界

类型的上界和下界,它们的含义如下。

1 U >: T

这是类型下界的定义,也就是U必须是类型T的父类(或本身,自己也可以认为是自己的父类)

 

2) S <: T

这是类型上界的定义,也就是S必须是类型T的子类(或本身,自己也可以认为是自己的子类)

协变、逆变结合上下

栗子

协变

逆变

trait c1[+T] {

def method[K >: T](x:K) = x }

trait c1[-T] {

def method [K <: T](x:K) = x }

object c2 extends c1[Int]

 

c2.method (3) // 3

c2.method (3.0) // 3.0

c2.method ("abc") // "abc"

object c2 extends c1[Int]

 

c2.method (3) // 3

c2.method (3.0) // 报错

c2.method "abc") // 报错

 


阅读更多
文章标签: 协变和逆变
个人分类: scala
所属专栏: Scala进阶专栏
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭