Java里Java泛型的作用有什么作用

本文为对JavaJava泛型的作用技术类型擦除部分的一个总结主要参考文献有《Java编程思想 第4版》、《Java核心技术 第10版》、《深入理解Java虚拟机 第2版》,文中的代码Demo主要来自于Java编程思想

下面是使用模板的C++示例。

当我们调用一个模板时C++编译器用实参来为我们推断模板实参,并为我们实例化(instantiate)一个特定版本的代码当編译器实例化一个模板时,它使用实际的模板实参代替对应的模板参数来创建出模板的一个新“实例”被编译器生成的版本通常称为模板的实例

对于上面代码在编译时,编译器会将T替换成HasF并生成模板实例类似于这样。

用C++编写这种代码很简单因为当模板实例化时,模板代码知道其模板参数的类型JavaJava泛型的作用就不同了。下面是HasF的Java版本

上面代码不能编译,报错

从上面报错信息可以看出编译器认为類型T没有f()方法。这是由于Java的Java泛型的作用是使用擦除来实现的意味着当你在使用Java泛型的作用时,任何具体的类型信息都被擦除了你唯一知道的就是你在使用一个对象。由于有了擦除Java编译器无法将manipulate()必须能够在obj上调用f()这一需求映射到HasF拥有f()这一事实上。

从表面上看Java的Java泛型的莋用类类似于C++的模板类,唯一明显的不同是Java没有专用的template关键字但是,其实这两种机制着本质的区别

Java语言的Java泛型的作用采用的是擦除法實现的伪Java泛型的作用,Java泛型的作用信息(类型变量、参数化类型)编译之后通通被除掉了使用擦除法的好处就是实现简单、非常容易Backport,運行期也能够节省一些类型所占的内存空间而擦除法的坏处就是,通过这种机制实现的Java泛型的作用远不如真Java泛型的作用灵活和强大Java选取这种方法是一种折中,因为Java最开始的版本是不支持Java泛型的作用的为了兼容以前的库而不得不使用擦除法。

Java泛型的作用类型只有在静态類型检查期间才出现在此之后,程序中的所有Java泛型的作用类型都将被擦除替换成它们非Java泛型的作用上界。

为了验证擦除我们编写下媔代码。

如果想要运行上面Java版本的HasF必须协助Java泛型的作用类,给定Java泛型的作用的边界以告知编译器只能接收遵循这个边界的类型。这里偅用了extends关键字(与类的继承有点类似但又不完全相同),给出类型的上界之所以称为上界,是通过继承树来考虑的对于继承树父节點在上,子节点在下那么extends关键字就限定了类型最多能上了继承树的什么地方,也就是上界由于有了边界,下面的代码就可以编译了

Java泛型的作用类型参数将擦除到它的第一个边界(可以有多个边界)。编译时Java编译器会将T擦除成HasF,就好像在类的声明中用HasF替换了T一样

可能就有小伙伴疑惑了,上面的Java泛型的作用好像并没有什么用我直接写下面这种手动擦除的代码不行吗。

这提出了很重要的一点:只有当伱希望使用的类型参数比某个具体类型(以及它的所有子类型)更加泛化化时——也就是说当你希望代码能够跨多个类工作时,使用Java泛型的作用才有所帮助

Java泛型的作用参数和他们在有用的Java泛型的作用代码中的应用,通常比简单的替换来的更复杂例如,如果某个类有一個返回T的方法那么Java泛型的作用就有所帮助,因为它们之后将返回确切的类型

例如对于下面两种写法,Java泛型的作用的写法可以不用强制轉换类型编译器会在编译期执行类型检查并插入转型代码。理解编译器对Java泛型的作用的处理非常重要

其实它们生成的字节码是完全相哃的。对进入set()的类型检查是不需要的因为这是由编译器执行的,而对从get()返回的值进行转型仍旧是需要的

可以看出,使用Java泛型的作用机淛编写的代码要比哪些杂乱地使用Object变量然后再进行强制类型转换的代码具有更好的安全性和可读性。Java泛型的作用是我们需要的程序设计掱段

擦除丢失了在Java泛型的作用代码中执行某些操作的能力。任何运行时都需要知道确切的类型信息的操作都无法工作

下面给出一些方法解决上面的问题。

  • 用工厂方法或模版方法创建类型实例

下面的两段代码是学习设计模式的好材料先来看工厂方法模式。

  • 用ArrayList代替数组戓者是传入类型标记

在JDK1.5后Signature属性被增加到了Class文件规范中,它是一个可选的定长属性可以出现在类、字段表和方法表结构的属性表中。在JDK1.5中夶幅度增强了Java语言的语法在此之后,任何类、接口、初始化方法或成员的Java泛型的作用签名如果包含了类型变量(Type Variables)或参数化类型(Parameterized Types)則Singature属性会为它记录Java泛型的作用签名信息。Signature属性就是为了弥补擦除法的缺陷而增设的Java可以通过反射获得Java泛型的作用类型,最终的数据来源吔就是这个属性

聂振宇2013年加入去哪儿网技术团隊,目前在平台事业部/基础架构部对并发编程,构建高并发系统很感兴趣

在我们平常写 Java 代码的过程中,总会或多或少的使用到 Java Java泛型的莋用但 Java Java泛型的作用有着各种各样的限制,运行时还会进行类型擦除在需要重度使用Java泛型的作用编程的场合,那酸爽真是一言难尽。 莋为曾经在 C++ 模版元编程中翱翔(挣扎)过的人曾经多次痛苦发出“这也不行?”“这样都不行?”“到底还能干啥?”的感慨不過工作还是要继续,虽然 Java Java泛型的作用不够完美我们还是要尽量寻找到它能够发光发热的地方。 这篇文章将分享一些在 Java Java泛型的作用上探索(折腾)的经验希望能对大家使用 Java Java泛型的作用起到一些帮助,有什么问题也欢迎指正

考虑这样一个简化的场景,应用会不断接收到 byte 数組每个数组第一个 byte 表示数据类型,剩下的部分是 json 序列化后的数据我们需要根据数据类型的不同来做一些对应的处理。

通过对场景的一頓分析我们写出了如下的代码(考虑到版面问题,省去了异常处理和一些显而易见的代码):

对象然后通过ParameterizedType的getActualTypeArguments方法获取Java泛型的作用参數(那对于没有定义父类的类,我们就不能获取到Java泛型的作用参数信息吗没错,好像只能放弃治疗) Java泛型的作用参数获取到之后,我們会发现 getActualTypeArguments() 返回的类型是 Type[]通过查找 jackson api,我们看到这个 Type 对象也可以起到类似

经过这次修改我们去掉了碍眼的 new TypeReference<List<String>>,可以看到现在一点儿重复代碼都没有了。 但是我们又发现了一个新的问题,如果像下面这么写的话是没有办法获取到Java泛型的作用参数的。

对于上面的 WrongProcessor<String> 类型对象来說由于Java泛型的作用擦除,它相当于是 WrongProcessor<Object> 对象type Capture 方法的结果会是通配符 T。而且这个问题在应用启动时不会出现并且显而易见会在序列化时絀现问题。我们能不能在程序启动时就发现这个问题呢

通过 debug captureType 方法返回的通配符 T,我们发现它属于 TypeVariable 接口这个接口专门用来描述通配符。洳果我们想要检测的话貌似在 captureType 函数里面判断一下就可以了。这样子应用启动过程中实例化对象构造函数就会抛出异常,防止错误代码啟动代码如下:

到这里似乎终于结束了,但是我们还要继续精益求精(瞎折腾

经过仔细分析后,最终版本如下:

    折腾到这里总算囿了一个较为完善且优雅的解决方案。Java Java泛型的作用的限制很多但只要我们愿意精益求精(折腾),还是能做出一些有用的东西来

可选中1个或多个下面的关键词搜索相关资料。也可直接点“搜索资料”搜索整个问题

你的钱包里你规定只装钱

那么你去出来的想也不想就是钱,管他是¥还是$不用转換的-->都是钱(确定里面是什么)

不用Java泛型的作用的话 就不规定放什么

那么你取出来的东西就不一定是钱,这样你就要转换了,那要是石头的话你要轉换成钱就错了(不确定里面到底是什么)

你对这个回答的评价是

我要回帖

更多关于 Java泛型的作用 的文章

 

随机推荐