泛型java博客园,Java深度历险之Java泛型
Java泛型(generics)是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter)。声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在JDK 5中的新集合类框架中。对于泛型概念的引入,开发社区的观点是褒贬不一。
从好的方面来说,泛型的引入可以解决之前的集合类框架在使用过程中通常会出现的运行时刻类型错误,因为编译器可以在编译时刻就发现很多明显的错误。而从不好的地方来说,为了保证与旧有版本的兼容性,Java泛型的实现上存在着一些不够优雅的地方。当然这也是任何有历史的编程语言所需要承担的历史包袱。后续的版本更新会为早期的设计缺陷所累。
开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收ListObject作为形式参数,那么如果尝试将一个ListString的对象作为实际参数传进去,却发现无法通过编译。虽然从直觉上来说,Object是String的父类,这种类型转换应该是合理的。但是实际上这会产生隐含的类型转换问题,因此编译器直接就禁止这样的行为。本文试图对Java泛型做一个概括性的说明。
类型擦除
正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的ListObject和ListString等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方式与C++模板机制实现方式之间的重要区别。
很多泛型的奇怪特性都与这个类型擦除的存在有关,包括:
泛型类并没有自己独有的Class类对象。比如并不存在ListString.class或是ListInteger.class,而只有List.class。
静态变量是被泛型类的所有实例所共享的。对于声明为MyClassT的类,访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过new MyClassString还是new MyClassInteger创建的对象,都是共享一个静态变量。
泛型的类型参数不能用在Java异常处理的catch语句中。因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyExceptionString和MyExceptionInteger的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常对应的catch语句。
类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般是Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。同时去掉出现的类型声明,即去掉的内容。比如T get()方法声明就变成了Object get();ListString就变成了List。接下来就可能需要生成一些桥接方法(bridge method)。这是由于擦除了类型之后的类可能缺少某些必须的方法。比如考虑下面的代码:
classMyStringimplementsComparableString{publicintcompareTo(String str) {return0;
}
}
当类型信息被擦除之后,上述类的声明变成了class MyString implements Comparable。但是这样的话,类MyString就会有编译错误,因为没有实现接口Comparable声明的int compareTo(Object)方法。这个时候就由编译器来动态生成这个方法。
实例分析
了解了类型擦除机制之后,就会明白编译器承担了全部的类型检查工作。编译器禁止某些泛型的使用方式,正是为了确保类型的安全性。以上面提到的ListObject和ListString为例来具体分析:
publicvoidinspect(ListObjectlist) {for(Object obj : list) {
System.out.println(obj);
}
list.add(1);//这个操作在当前方法的上下文是合法的。}publicvoidtest() {
ListStringstrs=newArrayListString();
inspect(strs);//编译错误}
这段代码中,inspect方法接受ListObject作为参数,当在test方法中试图传入ListString的时候,会出现编译错误。假设这样的做法是允许的,那么在inspect方法就可以通过list.add(1)来向集合中添加一个数字。这样在test方法看来,其声明为ListString的集合中却被添加了一个Integer类型的对象。这显然是违反类型安全的原则的,在某个时候肯定会抛出ClassCastException。因此,编译器禁止这样的行为。编译器会尽可能的检查可能存在的类型安全问题。对于确定是违反相关原则的地方,会给出编译错误。当编译器无法判断类型的使用是否正确的时候,会给出警告信息。
通配符与上下界
在使用泛型类的时候,既可以指定一个具体的类型,如ListString就声明了具体的类型是String;也可以用通配符?来表示未知类型,如List?就声明了List中包含的元素类型是未知的。 通配符所代表的其实是一组类型,但具体的类型是未知的。List?所声明的就是所有类型都是可以的。但是List?并不等同于ListObject。ListObject实际上确定了List中包含的是Object及其子类,在使用的时候都可以通过Object来进行引用。
而List?则其中所包含的元素类型是不确定。其中可能包含的是String,也可能是 Integer。如果它包含了String的话,往里面添加Integer类型的元素就是错误的。正因为类型未知,就不能通过new ArrayList?()的方法来创建一个新的ArrayList对象。因为编译器无法知道具体的类型是什么。但是对于 List?中的元素确总是可以用Object来引用的,因为虽然类型未知,但肯定是Object及其子类。考虑下面的代码:
publicvoidwildcard(List?list) {
list.add(1);//编译错误}
如上所示,试图对一个带通配符的泛型类进行操作的时候,总是会出现编译错误。其原因在于通配符所表示的类型是未知的。
因为对于List?中的元素只能用Object来引用,在有些情况下不是很方便。在这些情况下,可以使用上下界来限制未知类型的范围。 如List? extends Number说明List中可能包含的元素类型是Number及其子类。而List? super Number则说明List中包含的是Number及其父类。当引入了上界之后,在使用类型的时候就可以使用上界类中定义的方法。比如访问 List? extends Number的时候,就可以使用Number类的intValue等方法。
类型系统
在Java中,大家比较熟悉的是通过继承机制而产生的类型体系结构。比如String继承自Object。根据Liskov替换原则,子类是可以替换父类的。当需要Object类的引用的时候,如果传入一个String对象是没有任何问题的。但是反过来的话,即用父类的引用替换子类引用的时候,就需要进行强制类型转换。编译器并不能保证运行时刻这种转换一定是合法的。这种自动的子类替换父类的类型转换机制,对于数组也是适用的。 String[]可以替换Object[]。但是泛型的引入,对于这个类型系统产生了一定的影响。正如前面提到的ListString是不能替换掉ListObject的。
引入泛型之后的类型系统增加了两个维度:一个是类型参数自身的继承体系结构,另外一个是泛型类或接口自身的继承体系结构。第一个指的是对于 ListString和ListObject这样的情况,类型参数String是继承自Object的。而第二种指的是 List接口继承自Collection接口。对于这个类型系统,有如下的一些规则:
相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。即ListString是CollectionString 的子类型,ListString可以替换CollectionString。这种情况也适用于带有上下界的类型声明。
当泛型类的类型声明中使用了通配符的时候, 其子类型可以在两个维度上分别展开。如对Collection? extends Number来说,其子类型可以在Collection这个维度上展开,即List? extends Number和Set? extends Number等;也可以在Number这个层次上展开,即CollectionDouble和 CollectionInteger等。如此循环下去,ArrayListLong和 HashSetDouble等也都算是Collection? extends Number的子类型。
如果泛型类中包含多个类型参数,则对于每个类型参数分别应用上面的规则。
理解了上面的规则之后,就可以很容易的修正实例分析中给出的代码了。只需要把ListObject改成List?即可。ListString是List?的子类型,因此传递参数时不会发生错误。
开发自己的泛型类
泛型类与一般的Java类基本相同,只是在类和接口定义上多出来了用声明的类型参数。一个类可以有多个类型参数,如 MyClassX, Y, Z。 每个类型参数在声明的时候可以指定上界。所声明的类型参数在Java类中可以像一般的类型一样作为方法的参数和返回值,或是作为域和局部变量的类型。但是由于类型擦除机制,类型参数并不能用来创建对象或是作为静态变量的类型。考虑下面的泛型类中的正确和错误的用法。
classClassTestXextendsNumber, Y, Z{privateX x;privatestaticY y;//编译错误,不能用在静态变量中publicX getFirst() {//正确用法returnx;
}publicvoidwrong() {
Z z=newZ();//编译错误,不能创建对象}
}
最佳实践
在使用泛型的时候可以遵循一些基本的原则,从而避免一些常见的问题。
在代码中避免泛型类和原始类型的混用。比如ListString和List不应该共同使用。这样会产生一些编译器警告和潜在的运行时异常。当需要利用JDK 5之前开发的遗留代码,而不得不这么做时,也尽可能的隔离相关的代码。
在使用带通配符的泛型类的时候,需要明确通配符所代表的一组类型的概念。由于具体的类型是未知的,很多操作是不允许的。
泛型类最好不要同数组一块使用。你只能创建new List?[10]这样的数组,无法创建new ListString[10]这样的。这限制了数组的使用能力,而且会带来很多费解的问题。因此,当需要类似数组的功能时候,使用集合类即可。
不要忽视编译器给出的警告信息。
相关文章:

EXCEL-XML 代码相对行列转换绝对
这段时间因为工作的要求,需要在程序中处理xml 格式的excel 文档。但是在期间遇到了一个问题就是Row 和 Cell 的index 属性相对绝对的问题。 之前在bing 找了一下没找到介绍相对转换成绝对的资料。在经过多次的尝试和对比在空白的文档中不同的cell 中输入值后的XML文…

Integer 和 int 比较的特殊之处
2019独角兽企业重金招聘Python工程师标准>>> 第一个例子: 假设我们同时定义 int a 3; int b 3; 编译器先处理int a 3; 首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就…

偷偷mark下一个
java书单thinking in java java战 Effective Java 深入了解JVM虚拟机 java性能优化权威指南 JSR133 Google Guava官方教程版权声明:本文博主原创文章,博客,未经同意不得转载。 转载于:https://www.cnblogs.com/hrhguanli/p/4915607.html

unix odbc php 连接sqlserver,Ubuntu下通过unixODBC连接MS SqlServer2005
一、下载相关软件 unixODBC、freetds(1) Linux系统的ODBC unixODBC-2.2.8.tar.gz (http://www.unixodbc.org)(2) 连接SQLServer或Sybase的驱动 freetds-0.62.4.tar.gz (http://www.freetds.org)二、安装和配置(1) 安装unixODBC# tar vxzf unixODBC-2.2.8.tar.gz# cd un…

Apache htpasswd命令
一、简介 htpasswd是apache的一个工具,该工具主要用于建立和更新存储用户名、密码的文本文件,主要用于对基于http用户的认证。 二、语法 Usage:htpasswd [-cimBdpsDv] [-C cost] passwordfile usernamehtpasswd -b[cmBdpsDv] [-C cost] passwordfile use…

l2-22(重排链表)
题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805057860517888 题意:给定链表L1->L2->...->Ln,要求按Ln->L1->Ln-1->L2->...的格式输出。 思路:水模拟,按照要求做就…

深入研究敏捷的成功因素
Scott W. Ambler针对Dr. Dobbs网站上发布的敏捷成功因素进行了一些分析讨论,那些敏捷成功因素来自2011年11月的敏捷现状调查结果。据此文所说,此次调查的两个目标是: 为了探索与敏捷项目成功有关的实施策略,例如对单一敏捷团队的行…

php 多条数据更新数据类型,PHPdoc @param中的两个或多个数据类型
好的,我有这个phpdoc上面的我的类方法/*** this a function that translates the text* param string|boolean $lang if string the string given in the parameter will be the language code that will represent the language desired, if true, this will translate based …

快速构建Spring Cloud工程
spring cloud简介 spring cloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明spring cloud是基于spr…

win10红色警戒黑屏解决
相信很多80,90后的同学们都喜欢在代码之余打打红色警戒 但是苦于win10差劲兼容性,每次下个红色警戒or尤里复仇不是弹框就是黑屏 今天笔者给出一个完美究极解决方案 请注意: 1!红色警戒中把Ra2.exe和Game.exe右键兼容性调到Win XP …

html超链接button
1.如果让本页转向新的页面则用: <input typebutton οnclick"window.location.href(连接)"> 2.如果需要打开一个新的页面进行转向,则用: <input typebutton οnclick"window.open(连接)">转载于:https://www…

低版本jdbc连接高版本oracle,转:oracle11g的JDBC连接 URL和之前的版本有一定的区别...
今天安装了oracle11g后,写了JDBC测试程序,一直都连接不上!一直找不到原因后来读了一下安装文件中的Readme.txt文档,汗啊!这个版本居然把url的访问方式改变了:Some Useful Hints In Using the JDBC Drivers-…

Android studio 获取每次编译apk时的日期
项目中需要获取apk的编译日期,首先肯定是用手动的方式获取,但这样容易遗忘,怎么样通过代码的方式获取呢? 其实android 为我们提供了一个BuildConfig的类,android 每次编译的时候都会自动生成 一次BuildConfig 类&#…

明文存密码成惯例?Facebook 6 亿用户密码可被 2 万员工直接看
近日,外媒发布了一份互联网安全的调研报告,报告中称Facebook曾将6亿用户的账号密码使用明文存储,且可以被Facebook内部员工随意搜索查看。据Facebook方面的消息人士称,纯文本存档的用户密码可追溯到2012年,在这期间有超…

pthreads 的学习
多线程学习参考的网站: http://www.ibm.com/developerworks/cn/linux/l-pthred/ 初探线程——pthread_create http://www.cnblogs.com/huangwei/archive/2010/05/19/1739593.html 转载于:https://www.cnblogs.com/nemo2011/archive/2012/05/02/2479163.html

Oracle不加IP无法登录,Oracle 无法通过IP连接问题
1.安装目录:D:\app\Administrator\product\11.2.0\dbhome_1\NETWORK\ADMIN2.listener.ora(里面的localhost或127.0.0.1改成机器名)# listener.ora Network Configuration File: D:\app\Administrator\product\11.2.0\dbhome_1\NETWORK\ADMIN\listener.ora# Generated by Oracle…

巧用gh-pages分支发布自己的静态项目
大家都知道可以通过github pages 发布自己的静态博客,然后通过 username.github.io 可以访问。例如我的博客可以通过 nqmysb.github.io 访问,不过我的已经绑定域名 https://liaocan.top ,所以会直接跳转到域名显示。但是我们通常有很多其他的…

【读书笔记】iOS-网络-解析响应负载
Web Service可以通过多种格式返回结构化数据, 不过大多数时候使用的是XML与JSON。也可以让应用只接收HTML结构的数据。实现了这些Web Service或是接收HTML文档的应用必须能解释这种结构化数据并将其转换为对于应用上下文有意义的对象。 一,XML 使用原生解…

What Are Words(一诺千金)
曲名:What Are Words(一诺千金)Anywhere you are, I am near Anywhere you go, Ill be there Anytime you whisper my name, youll see How every single promise I keep Cause what kind of guy would I be If I was to leave when you need…

oracle 插入 基准测试,oracle proc 插入操作性能优化实践
场景:student 表中 10万条数据。从 student 表中取出所有数据,插入到 student_his 表中优化方法:1.批量插入(效果明显)2.批量查询(效果不明显)3.批量提交(效果不明显)4.预编译 sql 语句(效果不明显)效果:10万条数据,普…

240个jquery插件
240个jquery插件 http://www.kollermedia.at/archive/2007/11/21/the-ultimate-jquery-plugin-list/File upload Ajax File Upload.jQUploader.Multiple File Upload plugin.jQuery File Style.Styling an input type file.Progress Bar Plugin. Form Validation jQuery Valida…

sql 优化 tips
索引就是排序 outer join笛卡儿积, inner join看情况。 可以用临时表加update的方式把outer join 替换成inner join提高性能。用union代替where中的or 和join(不同表时)join的列有索引,select 中的列能被索引覆盖到,消除执行计划中的lookup(lookup有时会…

第24课 《前端之路,以不变应万变》
今天的内容有些借鉴于业内大佬的内容,由于本人技术实在太渣,几乎没有可以用来演讲的素材。抱歉 大家好,我是来自存勖科技的Rocken。我今天演讲的内容是:前端的未来。大家都知道,前端所依托的基础直到上世纪九十年代才出…

php hasattribute,PHP DOMElement hasAttribute()用法及代码示例
DOMElement::hasAttribute()函数是PHP中的内置函数,用于了解具有特定名称的属性是否作为元素的成员存在。用法:bool DOMElement::hasAttribute( string $name )参数:该函数接受单个参数$name,该参数保存属性的名称。返回值:如果成…

搭建turnserver
参考文件: http://blog.csdn.net/kl222/article/details/20145423 为什么要搭建TURN服务器? 因为我们编写的sip客户端再和南瑞的sip服务器进行通信的时候,中间经过一个安全平台,这个安全平台具有NAT和防火墙功能。RTP和RTCP包传递…

【Android开发】:在任意目录执行NDK编译
2019独角兽企业重金招聘Python工程师标准>>> 文以简单的例子讲述如何在任意目录把自己写的C代码使用NDK提供的交叉编译该工具来编译成Android可使用的静态库/动态库。 1. 准备环境 首先,你得安装了Android的NDK编译工具,假设你的NDK的根目录在…

SurfaceView 间取得焦点
在SurfaceView中我们的onKeyDown虽然重写了view的函数, 但一定需要我们在初始化的时候去声明焦点 //添加这个来取得按健事件this.setFocusable(true);this.setFocusableInTouchMode(true);this.requestFocus();如果这些方法,会造成按键无效,提…

Oracle字符串转BooIean,利用Java的多线程技术实现数据库的访问.pdf
利用Java的多线程技术实现数据库的访问.pdf第 卷第 期 计算机应用22 12 Voi .22 , No . 12年 月2002 12 Computer Appiications Dec . , 2002文章编号: ( )1001 - 9081 2002 12 - 0121 - 03利用Java 的多线程技术实现数据库的访问刘 巍,唐学兵(武汉大学 …

Linux音频设备驱动
在Linux中,先后出现了音频设备的两种框架OSS和ALSA,本节将在介绍数字音频设备及音频设备硬件接口的基础上,展现OSS和ALSA驱动的结构。17.1~17.2节讲解了音频设备及PCM、IIS和AC97硬件接口。17.3节阐述了Linux OSS音频设备驱动的组…

japid-controller自动绑定的数据类型
参考文献:http://www.playframework.org/documentation/1.2.3/controllers 当参数名和HTTP请求中的参数名(即界面中的name)相同时,后台Controller可以直接获取该变量的值。变量分两大类: 1. Simple types 所有的基本数…