如何深入理解Java泛型
一、泛型的作用与定义
1.1泛型的作用
-
使用泛型能写出更加灵活通用的代码
泛型的设计主要参照了C++的模板,旨在能让人写出更加通用化,更加灵活的代码。模板/泛型代码,就好像做雕塑时的模板,有了模板,需要生产的时候就只管向里面注入具体的材料就行,不同的材料可以产生不同的效果,这便是泛型最初的设计宗旨。 -
泛型将代码安全性检查提前到编译期
泛型被加入Java语法中,还有一个最大的原因:解决容器的类型安全,使用泛型后,能让编译器在编译的时候借助传入的类型参数检查对容器的插入,获取操作是否合法,从而将运行时ClassCastException转移到编译时比如:
List dogs =new ArrayList();
dogs.add(new Cat());
在没有泛型之前,这种代码除非运行,否则你永远找不到它的错误。但是加入泛型后
List<Dog> dogs=new ArrayList<>();
dogs.add(new Cat());//Error Compile
会在编译的时候就检查出来。
- 泛型能够省去类型强制转换
在JDK1.5之前,Java容器都是通过将类型向上转型为Object类型来实现的,因此在从容器中取出来的时候需要手动的强制转换。
Dog dog=(Dog)dogs.get(1);
加入泛型后,由于编译器知道了具体的类型,因此编译期会自动进行强制转换,使得代码更加优雅。
1.2泛型的定义
自JDK 1.5 之后,Java 通过泛型解决了容器类型安全这一问题,而几乎所有人接触泛型也是通过Java的容器。那么泛型究竟是什么?
泛型的本质是参数化类型
也就是说,泛型就是将所操作的数据类型作为参数的一种语法。
其中T就是作为一个类型参数在Play被实例化的时候所传递来的参数,比如:
这里T就会被实例化为Integer
二、通配符与嵌套
2.1通配符
?:表示类型不确定,只能用于声明变量或者形参上,不能用在创建泛型类,泛型方法和接口上
2.2泛型嵌套
定义两个泛型类,Myclass类的泛型就是student类,而student类的泛型是String类
如上就实现了泛型的嵌套,在HsahMap中对键值对进行便利的时候,也利用了泛型的嵌套
三、泛型的上下边界
? extends E 是 泛型 的上边界 , ? super T 是 泛型的下边界
3.1 < ? extends E >
List < ? extends A > 表示 这个list里面存的是A的子类,具体是啥不知道,只知道范围!
那可以设定分两个范围: A子类,A父类。
对于add:
A子类情况:如果你允许add的元素是A的子类,那么因为泛型参数限定? extends A,所以ArrayList可以指定的类型也是A的子类,那么无法保证add的对象一定ArrayList指定类型的子类对象,比如: ArrayList指定C, list却add了A()对象–这就是非法的!(因为list并不知道指定了C,它可以确定的范围就是可以add所有A的子类。)所以A子类该范围不行!
A父类情况:ArrayList指定类型肯定实在(?,A]范围内,所以这种情况肯定不行!
对于get:
list不知道你存入ArrayList是什么类型,但是我可以确定使用A的引用肯定可以接受ArrayList中的元素(无论ArrayList设定什么类型),因为A肯定是里面元素的父类!但是你使用其他类型接受就不行了,因为如果ArrayList的类型是你指定的那个类型的父类,是接受不了的。
例子:BaseStudent extends Student 。
代码如下:
list3<BaseStudent> 和 list2<Student> 的 对象元素 都添加到了 list1<Student>中了。
3.2< ? super E >
List < ? super C > 表示list里面存的是C和其父类,具体是啥不确定,只知道范围。
- 同样的分为:C子类,C父类。(其实应该发现了没必要分为两类,有一类是肯定不行的)。
- add:
- C子类:如果允许add,就允许list来add任何C的子类元素,因为ArrayList指定的范围是C和其父类,所以ArrayList可以准确接受该假设范围的所有对象。 所以,C子类这个范围可行,这样就有一个标准的类型判断依据,不像extends,根本无法确定判断标准,编译器就不会知道怎么做。
- C父类:显然和上面A子类情况是一样的!因为list无法知道ArrayList指定类型 ,所以无法确定判断标准。
- get:
- 因为list只允许add C的子子类对象,你或许也在想:那我直接用C不就可以接受ArrayList数据吗?并不是的,ArrayList在赋给list之前可能里面已经有值了,这个值的类型如果是ArrayList指定类型,并且是C的父类,那么C就无法接受! 而且list不知道ArrayList会指定哪个类型,只知道范围,所以无法确定是哪个父类,所以干脆用Object那肯定就能接受了,所以返回的其实只有Object能接受,否则就得强转。
四、 RxJava中深入理解泛型
<meta charset="utf-8">
4.1 响应式编程:
4.2观察者模式:
4.3RxJava是对观察者模式的一种高级运用,或者说是一种升级,他把观察者模式具体化,更加明确了各个对象之间的关系
谈完了响应式的一些东西,我觉得既然要讨论学习泛型的使用,我们就把泛型的一些概念也揪出来瞅一下
贴个代码看一下
大概了解一下泛型的作用域和泛型的类型之后,我们现在有这么一个需求
我给你一个对象,你能够观察着该对象,即一个观察者中存在着该对象的引用,并且将该观察者返回给我
我刚开始是这么想的,我们看一下有没有什么问题
看代码的话好像确实也没什么问题,我把泛型的声明放在了类上,那我这个类中都是可以识别T类型的,那我在创建对象的时候传入T好像也没什么不对,一样完成了需求,我们回到创建该对象的main方法中去看一看,创建方法变成了这样
我去翻了翻Rxjava的源码,他将Observable这个对象的构造函数的访问权限降低了,不在他包下都不可以创建这个对象,但是他提供了一个create方法去创建,我们也来模仿一下
创建方法变成了这样
这样我们在使用ImplObserver的时候就没有对这个类的泛型进行明确说明,而是在create方法中进行了声明,怎么声明的? 这里面还有点门道,我们将create方法定义成了静态方法,并且在该方法上声明了T类型,这样该方法的T类型就会隐藏掉类上的T类型,但是,我们的create方法做了这么一件事,将静态方法的泛型,传递给了ImplObservable类上的泛型,并且返回创建好的ImplObservable泛型对象,此处的泛型类型为create方法声明的泛型类型
是不是有点晕了?我当时也是晕的不行,迷糊过来之后也就那样吧..如果有迷糊的朋友在下方评论吧,指出你的问题,我们一起讨论
现在来考虑Rxjava写代码舒服的原因,全链式,全链式啊有木有,一条道走到黑,就是在不停的调用调用调用,不需要我们去考虑返回的对象是什么对象,只需要进行一系列操作就可以了,因为泛型已经帮助我们做了太多太多.
链式?哇,链式调用好像是很牛逼的,我们也来实现一下.
先说一下需求:
说实话我是在学习泛型,研究Rxjava,我为啥非得给自己找不自在,提出的需求比较恶心就算了,还并且,俩并且,完成功能不就行了吗?追求那么点的链式可能会给我的工作,我的业余时间带来什么呢?
好了,我们来分析一下需求:
可能你看这点代码会比较恶心,甚至会吐..
先喝杯水,起来晃悠一下,放松一会,希望你待会能打起十二分精神来读接下来的一丁点篇幅
我会认真将自己的理解全部写出来.
好了放松一下吧...确实比较恶心,也有点绕口,烧脑,但是想通了也就是那么一回事...
现在再来定义一个操作符,我们就结束今天这篇文章了
需求是这样的
分析一下:
代码实现:
最后上一下测试代码
参考:https://www.jianshu.com/p/6130ff181d48
https://blog.csdn.net/Justin_zhao/article/details/77750440
https://blog.csdn.net/Ameir_yang/article/details/81514078
https://blog.csdn.net/Bazingaea/article/details/51150380