二、实现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的逆变和协变的介绍,下篇文章我们深入进行使用逆变和协变