【Java入门提高篇】Day14 Java中的泛型初探
泛型是一个很有意思也很重要的概念,本篇将简单介绍Java中的泛型特性,主要从以下角度讲解:
1.什么是泛型。
2.如何使用泛型。
3.泛型的好处。
1.什么是泛型?
泛型,字面意思便是参数化类型,平时所面对的类型一般都是具体的类型,如果String,Integer,Double,而泛型则是把所操作的数据类型当作一个参数。如,ArrayList<String>(),通过传入不同的类型来指定容器中存储的类型,而不用为不同的类型创建不同的类,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
2.如何使用泛型?
我们先来看看泛型是什么样子的:
- public interface List<E> {
- void add(E);
- Iterator<E> iterator();
- }
这是List接口,这里用E来代替具体类型,这样就可以往里面传入任意类型,也许你要问了,直接使用Object不好吗?我们来用一个栗子比较一下:
先用非泛型方式来实现一下:
- public class ObjHolder {
- private Object a;
- public ObjHolder(Object a) {
- this.a = a;
- }
- public void set(Object a) {
- this.a = a;
- }
- public Object get(){
- return a;
- }
- public static void main(String[] args) {
- ObjHolder holderA = new ObjHolder("Frank");
- System.out.println((String) holderA.get());
- holderA.set(233);
- System.out.println((Integer) holderA.get());
- }
- }
这样就实现了一个包装类,可以用来存取一个任意类型的对象。但是每次取出来都需要进行类型转化,如果方法的参数类型是ObjHolder的话,无法知道它里面存放的对象的确切类型,这样就反而带来很多不必要的麻烦。
现在来看一下用泛型实现是怎样的:
- public class GenericHolder<T> {
- private T obj;
- public GenericHolder(T obj){
- this.obj = obj;
- }
- public T getObj() {
- return obj;
- }
- public void setObj(T obj) {
- this.obj = obj;
- }
- public static void main(String[] args) {
- GenericHolder<String> holderA = new GenericHolder<String>("Frank");
- System.out.println(holderA.getObj());
- //holderA.set(233);无法编译通过,因为只能往holderA中存入String类型
- GenericHolder<Integer> holderB = new GenericHolder<Integer>(233);
- System.out.println(holderB.getObj());
- }
- }
这样通过传入类型信息如String和Integer,来代替其中的泛型参数T,这里的T可以理解为一个占位符,用其他字母也是可以的,一旦传入具体类型,如String,则所有使用T的地方都会用String类型替换。
对比一下上面两种方式,区别在哪呢?打个比方,不用泛型的实现方式,相当于一个袋子,里面可以装任意类型的黑盒子,你什么都可以往里放,但是你可能不知道你下一个取出来的是什么东西,而泛型的实现方式,相当于一个贴了标签的黑盒子,标签上可以写任何信息,如写上水果,那么这个盒子就只能装水果,你也会知道每次取出来的肯定是水果而不是其它东西,同理类似如写上杂粮,那么这个袋子就只能用来装杂粮,但其实上都是同一种袋子,并不是为每一种类型的东西准备一种袋子。(因为Java的泛型使用了类型擦除机制,至于类型擦除是什么,暂时不做过多介绍,以后会有文章做更详细的说明)。
泛型被广泛应用在容器类中,如ArrayList<T>() 表示用于存储特定类型的数组,除此之外,还有很多泛型接口,如Comparable<T>。使用泛型能带来极大的便利性。
在泛型中可以对类型进行限制,如:<T extends Comparable<T>>表示只能传递已经实现了Comparable接口的类型对象,这里是使用extends而不是implement,而且对于接口也只能写一个。<T extends Number>表示只能接收Number类或者其子类的对象。与之相反的边界通配符是super,如:<T super Phone>表示只能接收类型为Phone或其父类的对象。
在使用extends和super的时候需要特别注意,因为使用它们是有副作用的,比如:
- List<T extends Number> list = new ArrayList<Number>();
- list.add(4.0);//编译错误
- list.add(3);//编译错误
因为泛型是为了类型安全设计的,如果往List<? extends Number> list 塞值的话,在取的时候就无法确认它到底是什么类型了,编译器只知道它是Number类型或者它的派生类型,但无法确定是哪个具体类型。通配符T表示其中存的都是同一种类型,因此使用extend下边界的话是无法进行存操作的。同理super下边界是不能取值的。
那什么时候该用extends,什么时候该用super呢?先说结论:
PECS原则:
- 频繁往外读取内容的,适合用上界Extends。
- 经常往里插入的,适合用下界Super。
3.泛型的好处?
泛型看起来很炫酷,但初看起来,好像没什么卵用?客官且慢,进屋里坐(滑稽)。
使用泛型的好处我们来一项一项列出来:
1,类型安全。
这是最显而易见的,泛型的主要目标是提高 Java 程序的类型安全。通过使用泛型定义的变量的类型限制,可以很容易实现编译期间的类型检测,避免了大量因为使用Object带来的不必要的类型错误。
没有泛型,这些对Object变量的类型假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中),而且每次使用前还需要进行不安全的强制类型转换。
2,代码复用。
泛型的一个很大好处就是增加了代码的复用性,比如上面的 GenericHolder 类,就能存取任意类型的对象,而不用为每种类型写一个包装类。
3,潜在的性能收益。
泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
至此,本篇讲解完毕,如果想要更好的理解,还需要多写代码,在实践中去应用。
欢迎大家继续关注!
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan
【Java入门提高篇】Day14 Java中的泛型初探的更多相关文章
- 【Java入门提高篇】Java集合类详解(一)
今天来看看Java里的一个大家伙,那就是集合. 集合嘛,就跟它的名字那样,是一群人多势众的家伙,如果你学过高数,没错,就跟里面说的集合是一个概念,就是一堆对象的集合体.集合就是用来存放和管理其他类对象 ...
- 【Java入门提高篇】Day13 Java中的反射机制
前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上 ...
- 【Java入门提高篇】Day1 抽象类
基础部分内容差不多讲解完了,今天开始进入Java提高篇部分,这部分内容会比之前的内容复杂很多,希望大家做好心理准备,看不懂的部分可以多看两遍,仍不理解的部分那一定是我讲的不够生动,记得留言提醒我. 好 ...
- 【Java入门提高篇】Day21 Java容器类详解(四)ArrayList源码分析
今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作. 本篇的内容主要包括这几块: 1.源码结构介绍 2.源 ...
- 【Java入门提高篇】Day16 Java异常处理(下)
今天继续讲解java中的异常处理机制,主要介绍Exception家族的主要成员,自定义异常,以及异常处理的正确姿势. Exception家族 一图胜千言,先来看一张图. Exception这是一个父类 ...
- 【Java入门提高篇】Day31 Java容器类详解(十三)TreeSet详解
上一篇很水的介绍完了TreeMap,这一篇来看看更水的TreeSet. 本文将从以下几个角度进行展开: 1.TreeSet简介和使用栗子 2.TreeSet源码分析 本篇大约需食用10分钟,各位看官请 ...
- 【Java入门提高篇】Day28 Java容器类详解(十)LinkedHashMap详解
今天来介绍一下容器类中的另一个哈希表———>LinkedHashMap.这是HashMap的关门弟子,直接继承了HashMap的衣钵,所以拥有HashMap的全部特性,并青出于蓝而胜于蓝,有着一 ...
- 【Java入门提高篇】Day27 Java容器类详解(九)LinkedList详解
这次介绍一下List接口的另一个践行者——LinkedList,这是一位集诸多技能于一身的List接口践行者,可谓十八般武艺,样样精通,栈.队列.双端队列.链表.双向链表都可以用它来模拟,话不多说,赶 ...
- 【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析
前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了.别慌别慌,这一篇来说说集合框架里最偷懒的一个家伙——HashSet,为什么说它是最偷懒的呢,先留个悬念,看完 ...
随机推荐
- 洛谷 [P1020] 导弹拦截 (N*logN)
首先此一眼就能看出来是一个非常基础的最长不下降子序列(LIS),其朴素的 N^2做法很简单,但如何将其优化成为N*logN? 我们不妨换一个思路,维护一个f数组,f[x]表示长度为x的LIS的最大的最 ...
- demo说明
访问http://192.168.90.63:30111/face_mark/, 会看到上图的界面. 下面简单说下如何使用这个demo. 一.选择选择同一个人不同角度的五张图. 选中了五张图片.此时任 ...
- 2017人生总结(MECE分析法)
试着用MECE分析法对人生的整个规划做一下总结.作为技术人员,其实除了编码架构能力之外,分析问题的能力的重要程度也会随着职业发展越来越重要.<美团点评技术博客>说这几天要在黄金时段头版头条 ...
- Orleans之EventSourcing
Orleans之EventSourcing 这是Orleans系列文章中的一篇.首篇文章在此 引入: 如果没有意外,我再这篇文章中用ES代替EventSourcing,如果碰到"事件回溯&q ...
- CentOS7 修改网卡名称为eth0
前言 无论是RHEL 7.还是CentOS 7都使用了NetworkManager.service来进行网络管理,当然network服务还是可以继续使用的,但也将会是过渡期的残留品了. 除此之外7版本 ...
- CUP、内存、磁盘是如何在一起工作的
IT技术发展到今天,计算机能做的事情可谓复杂的多.那么计算机是如何做出如此复杂的运算的呢? 不准确的说,计算机主要做两件事,数据计算和数据存储. 第一先说说计算机是如何计算的吧. 我们平时见到的所有计 ...
- ASP.NET Core 2.0 : 五.服务是如何加载并运行的, Kestrel、配置与环境
"跨平台"后的ASP.Net Core是如何接收并处理请求的呢? 它的运行和处理机制和之前有什么不同? 本章从"宏观"到"微观"地看一下它的 ...
- session垃圾回收机制
主要有以下三个参数 session.gc_maxlifetime:session生命周期 session.gc-devisor:启动session回收机制频率的被除数(分母) session.gc_p ...
- 批处理文件:windows下关闭指定端口
@echo offsetlocal enabledelayedexpansionset /p port=please input port number:for /f "tokens=1-5 ...
- JPA数据懒加载LAZY和实时加载EAGER(二)
懒加载LAZY和实时加载EAGER的概念,在各种开发语言中都有广泛应用.其目的是实现关联数据的选择性加载,懒加载是在属性被引用时,才生成查询语句,抽取相关联数据.而实时加载则是执行完主查询后,不管是否 ...