十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
这篇文章跟大家分析一下“Java高级特性中的泛型、反射和注解该如何理解”。内容详细易懂,对“Java高级特性中的泛型、反射和注解该如何理解”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅读后能够对大家有所帮助。下面跟着小编一起深入学习“Java高级特性中的泛型、反射和注解该如何理解”的知识吧。
目前创新互联已为成百上千家的企业提供了网站建设、域名、网络空间、网站运营、企业网站设计、泰州网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
一、泛型介绍
在日常编程的过程中,泛型在这三个特性之中使用频率是最高的。”泛型”一词中的泛字可以理解为泛化的意思,即由具体的、个别的扩大为一般的。Oracle对泛型的官方定义是:泛型类型是通过类型参数化的泛型类或接口。一言以蔽之,泛型就是通过类型参数化,来解决程序的通用性设计和实现的若干问题。
Java泛型是1.5版本后引入的特性,它主要被用于解决三类问题:
1、编译器类型检查
例如上图中的实例1设计了一个简单的Box类,在其中定义了一个private的object的属性,同时定义了get()和set()两个行为,其中set()用于保存object到Box内,set()用于获取Box中的object对象。从抽象的角度看,Box类抽象了一个用于在盒子中存放物品对象和存取的行为,存取的方法接受或者返回Object类型的对象。在这个抽象的基础上,可以存放除原始类型外任意类型的对象,Object类型的声明体现了面向对象中继承的理念。
在实例2中,实现了不同业务场景下对Box的使用方式。其中列举了两种不同的业务场景,场景一需要在Box中存放String类型的对象,场景二需要在Box中存放Integer类型的对象,这种情况下,在实际开发时,场景二中很有可能会错误地传入一个String对象,导致运行时错误的发生,而这正是因为Box可以被只有传入任意类型的对象导致的,这种情况在集合类操作时尤为突出。例如实例3中的情况:
首先声明了一个List类型的boxes对象,其中存放了两个对象,一个是String类型的“aaaaa”,另一个是Integer类型的11111。在业务场景一下,使用者认为boxes中存放的所有对象都是String类型的,因此在取出第二个对象并进行类型转换的时候就发生了错误。这种情况往往让使用者十分迷惑,明明编译时没有问题,但是在运行时却产生了异常。也就是说,在这种面向对象的抽象过程中,无法通过编译来验证类型该如何进行使用。
那么泛型是如何解决这类问题的呢?
Oracle意识到了上述的问题,在引入泛型之后,通过将代码中的“public class Box”更改为“public class Box
乍一看类型变量这个词,感觉有点晦涩难懂,但其实如果仔细思量一番会发现它其实并不难理解,上面的实例4可以理解为“在使用泛型时,可以将类型参数T传递给Box类型本身”,结合Oracle给出的官方定义“泛型的本质是类型参数化”会有更深的理解。
在实例5中,在对象声明和初始化的时候,都指定了类型参数T,在场景一种,T为String;在场景二中,T为Integer。这样,在场景二中向IntegerBox中传入String类型的数据“aaaaa”时,程序会报错。实例6中的泛型集合对象的操作也与之类似,在声明了一个List
可以看到,通过对于泛型的使用,之前的多业务场景中的问题都得到了解决,因为现在在编译阶段就可以解决之前类型不匹配的问题,而不用等到运行时才暴露问题,只要合理使用泛型,就能在很大程度上规避此类风险。对于泛型的使用,这种参数化类型的作用表面上看是声明,背后其实是约定。
2、强制类型转换
再回顾一下实例3,在List类型的boxes对象中存放了两个对象,分别是String类型的“aaaaa”和Integer类型的11111。其中存在一个问题,在对于boxes的声明中,使用者不知道boxes的list中到底应该存放什么类型的对象,而编译器也不知道集合存放的数据类型,只能通过实际的业务场景来决定这个box是什么类型,采用将Object强制转换成String的方式,来达到业务要求的效果。
在使用泛型之后,解决了这种场景下必须进行强制类型转换的问题。如实例7中,通过泛型声明,指定集合内元素的类型参数为String类型,这样编译器就直接知晓了元素的类型,而无需依靠实际的业务逻辑进行转换,从而解决了这类类型强制转换的问题。
3、可读性和灵活性
泛型除了能进行编译器类型检查和规避类型强制转换外,还能有效地提高代码的可读性。对于实例3,如果不使用泛型,当一个不清楚业务场景的人在对集合进行操作时,无法知道list中存储的是什么类型的对象,如果使用了泛型,就能够通过其类型参数判断出当前的业务场景,也增加了代码的可读性,同时也可以大胆地在抽象继承的基础上进行开发了。
泛型使用上的灵活性体现在很多方面,因为它本身实质上就是对于继承在使用上的一种增强。因为泛型在具体工作时,当编译器在编译源码的时候,首先要进行泛型类型参数的检查,检查出类型不匹配等问题,然后进行类型擦除并同时在类型参数出现的位置插入强制转换指令,从而实现泛型。
除了上述的基础用法之外,泛型还有几种特殊的高阶用法:
通配符的设计存在一定的场景,例如在使用泛型后,首先声明了一个Animal的类,而后声明了一个继承自Animal类的Cat类,显然Cat类是Animal类的子类,但是List
1、 上界通配符
上界通配符顾名思义,表示的是类型的上界(包含自身),因此通配的参数化类型可能是T或T的子类。正因为无法确定具体的类型是什么,add方法受限(可以添加null,因为null表示任何类型),但可以从列表中获取元素后赋值给父类型。如上图中的第一个例子,第三个add()操作会受限,原因在于List
2、 下界通配符
下界通配符表示的是参数化类型是T的超类型(包含自身),层层至上,直至Object,编译器无从判断get()返回的对象的类型是什么,因此get()方法受限。但是可以进行add()方法,add()方法可以添加T类型和T类型的子类型,如第二个例子中首先添加了一个Cat类型对象,然后添加了两个Cat子类类型的对象,这种方法是可行的,但是如果添加一个Animal类型的对象,显然将继承的关系弄反了,是不可行的。
3、 无界通配符
在理解了上界通配符和下界通配符之后,其实也自然而然的理解了无界通配符。无界通配符用表示,?代表了任何的一种类型,能代表任何一种类型的只有null(Object本身也算是一种类型,但却不能代表任何一种类型,所以List