二、实现scala的逆变和协变类

引用文章:

https://blog.csdn.net/luofazha2012/article/details/80342579

https://blog.csdn.net/pengych_321/article/details/51259002

 

一、前提引入

我们都知道scala中可以定义类型,以及定义类继承关系。如下代码,我们定义两个类,并且有继承关系:

package java_genericity

object Scala_Genericity {
  def main(args: Array[String]): Unit = {

    var cat = new Cat
    cat.eat()

    var tiger = new Tiger
    tiger.eat()

    cat = tiger
    cat.eat()
  }
}

class Cat{
  def eat(): Unit ={
    println("我吃火腿")
  }
}

class Tiger extends Cat{
  // 加override  覆写父类方法
  override def eat(): Unit ={
    println("我吃肉")
  }
}

上述输出大家肯定都清楚,按照调用顺序输出:

我吃火腿
我吃肉
我吃肉

 二、深入剖析

此时我想验证如果使用参数化类型的接口或者类,是否可以继续实现继承关系。比如,我定义了list,然后初始化a=list[cat],B=list[tiger] ,那么还会有上述结果吗?实验一下

package java_genericity

object Scala_Genericity {
  def main(args: Array[String]): Unit = {

    var cat = new Cat
    cat.eat()

    var tiger = new Tiger
    tiger.eat()

    cat = tiger
    cat.eat()

    var catList = List[Cat]
    var tigerList = List[Tiger]

    catList = tigerList
  }
}

class Cat{
  def eat(): Unit ={
    println("我吃火腿")
  }
}

class Tiger extends Cat{
  // 加override  覆写父类方法
  override def eat(): Unit ={
    println("我吃肉")
  }
}

 

此时我们会发现报错:

 

 

 如果想要解决这个问题的话,我们需要如下操作:我们自己定义一个支持转化的参数化类型的类。这种写法可以支持我们转化操作。在scala中称之为协变。

package java_genericity

object Scala_Genericity {
  def main(args: Array[String]): Unit = {

    var cat = new Cat
    cat.eat()

    var tiger = new Tiger
    tiger.eat()

    cat = tiger
    cat.eat()

    var catList = List[Cat]
    var tigerList = List[Tiger]

    //报错  编译无法通过
    catList = tigerList

    var catCovariant = new Covariant[Cat]
    var tigerCovariant = new Covariant[Tiger]
    catCovariant = tigerCovariant
  }
}

class Cat{
  def eat(): Unit ={
    println("我吃火腿")
  }
}

class Tiger extends Cat{
  // 加override  覆写父类方法
  override def eat(): Unit ={
    println("我吃肉")
  }
}

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

 

 

那假如我们突发奇想,想把cat类型赋给tiger类型。首先tiger是cat的子类,我们想如果想赋值成功的话,就相当于是把cat看成tiger的子类了。所以我们现在想干的事就是:tiger extends cat===》class[cat] extends clapackage java_genericity


object Scala_Genericity {
  def main(args: Array[String]): Unit = {

    var cat = new Cat
    cat.eat()

    var tiger = new Tiger
    tiger.eat()

    cat = tiger
    cat.eat()

    var catList = List[Cat]
    var tigerList = List[Tiger]

    //报错  无法通过编译
    tigerList = catList

    var catcontrovariant = new controvarine[Cat]
    var tigercontrovariant = new controvaiane[Tiger]
    tigercontrovarane = catcontrovaiane //编译通过
  }
}catInversion

class Cat{
  def eat(): Unit ={
    println("我吃火腿")
  }
}

class Tiger extends Cat{
  // 加override  覆写父类方法
  override def eat(): Unit ={
    println("我吃肉")
  }
}

/**
  * 定义一个支持逆变的类
  * @tparam T
  */
class controvaiane[-T]{}

 

 三、回顾

java中是不支持这样的写法的,因为java中对于类型安全有要求,我们不能这样随意玩弄类型,并赋值给其他类。不过java有另外的可实现的方法。此处不予介绍。为什么java有类型安全问题,scala没有呢。因为scala中定义的类一般都是不可变的。初始化是什么就是什么。所以不用担心会往里面放入导致类型不安全的值。举个例子,java的obj = list<object>,str = list<string>,如果java支持协变。那么把obj = str。那么我是可以obj.add(new date()),但是实际上是不可以的,因为此时obj内部引用地址是string的链表。如果我们可以插入date类型的话,那么java保持的类型安全就没有意义了。又或者,我们需要从obj里面取出数据,取出来的如果都不是string'类型,那么也是不合适的。因为引用的是string链表,取出来的都可以不是string类型,那也不应该对吧。

 

留言:这篇文章是简单的做了一个scala的逆变和协变的介绍,下篇文章我们深入进行使用逆变和协变

 

posted @ 2020-07-25 20:40  逆水行舟DOIT  阅读(43)  评论(0编辑  收藏  举报