JDK1.5新特性(六)……Generics
概述
Generics - This long-awaited enhancement to the type system allows a type or method to operate on objects of various types while providing compile-time type safety. It adds compile-time type safety to the Collections Framework and eliminates the drudgery of casting.
泛型的出现主要是为了解决集合类元素的类型规范,即将集合类参数化,传入规定的元素类型参数,规范其元素的类型,这样就避免了很多类型转换上的异常,下面我们由浅入深,慢慢来介绍
泛型的简单应用
虽然泛型的底层实现略有不爽,但是在表层的使用上还是很好理解的,至少,一些简单的使用就可以解决一大部分问题
下面我们来看一下
过去我们使用集合类来存储和获取对象时是这样做的
1: public static void main(String[] args) {
2: // TODO Auto-generated method stub
3:
4: //过去使用集合类的方法
5: List list = new ArrayList();
6: list.add(1);
7: list.add(2);
8: list.add("1");
9: Integer retVal = (Integer)list.remove(2);
10: System.out.println(retVal);
11: }
我们看到,过去必须手动的进行类型转换,这样就很容易出现ClassCastException异常
加入泛型之后,我们这样做
1: public static void main(String[] args) {
2: // TODO Auto-generated method stub
3:
4: //JDK1.5之后使用集合类的方法
5: List<Integer> list = new ArrayList<Integer>();
6: list.add(1);
7: list.add(2);
8: list.add("1");
9: Integer retVal = list.remove(2);
10: System.out.println(retVal);
11:
12: }
可以看到,List之后尖括号中存放了规定的元素类型,这样只能将Integer对象作为元素传入到List集合中,而当出现其他类型时,编译会出错,这样就有效的将运行时的异常转变为了编译时期的错误,提高了系统的安全性
了解泛型
涉及到的术语
上面的例子中
List和ArrayList被称为原始类型(raw type)
而List<Integer>和ArrayList<Integer>被称为参数化的类型(parameterized type)
其中Integer叫做实际类型参数
原始类型与参数化类型的兼容性
List<Integer> list = new ArrayList();//参数化类型可以接收一个原始类型对象
List l = new ArrayList<String>();//原始类型对象可以接收一个参数化类型对象
之所以会出现这种情况,我想与其底层实现不无关联,等下会提到
看一个例子
1: public static void main(String[] args) {
2: // TODO Auto-generated method stub
3:
4: //JDK1.5之后使用集合类的方法
5: List<Integer> list = new ArrayList();
6: List l = new ArrayList<String>();
7: list.add(1);
8: list.add(2);
9: l.add(1);
10: Integer retVal = (Integer)l.remove(0);
11: System.out.println(retVal);
12:
13: }
由这个例子可知,实际参数只定义在右边是没有什么意义的,在编译阶段,如果左边是原始类型,编译器是不会判断传入的类型的,如果左边是参数化类型,才会判断
参数化类型并不支持类型的继承关系
也就是说
List<Integer> l = new ArrayList<Object>();
List<Object> l = new ArrayList<Integer>();
两种编译器都不支持,两个类型mismatch.
但是如果是这样定义的,就不会报错
List l = new ArrayList<Object>();
List<Integer> l = l;
为什么会是这样?原因是泛型的实现机制:
因为泛型的语法判断是在编译阶段,泛型的定义只保留在编译阶段,在真正的运行阶段,会将泛型的定义擦除,也就是说List<Integer> 与List<String>在底层其实是共用的一份字节码,所以在Java中的泛型实现跟C++中的模板是有本质区别的,那么为什么不保留泛型定义到运行阶段呢?这是因为Java是解释型语言,编译器生成的字节码文件是可以跨平台的,在不同的平台对应不同的JVM,JVM会将同一份字节码翻译成针对不同平台的二进制指令,那么如果将泛型保留到运行时期,JVM需要做大量的指令集的重构,这个工程非常的浩大,同时,也是为了兼容原来的原始类型,这是在设计历史上必须承担的后果,所以,这种的办法就是将泛型只保留在编译时期,让编译器判断语法正误,而在运行时期,进行类型擦除,当然,在泛型的实现上有很多不够优雅的地方,在业内也褒贬不一,我想这也是任何有历史的编程语言都需要承担的吧。
Java不支持定义参数化类型数组
如
Vector<Integer> vectorList[] = new Vector<Integer>[10]//这样会报错
泛型的定义
参数化类型其实就是将一种数据类型也作为一个参数传递给一个原始类型,那么我们也可以定义自己的带有泛型参数的东西
泛型类的定义
什么时候定义泛型类?
当类中要操作的某些参数的数据类型不确定时,JDK1.5之前是使用Object定义,例如Ojbect的toString方法
JDK1.5之后我们可以用泛型来定义,通常用单个的大写字母表示一个泛型
1: public class MyGenericClass<T> {
2: private T x;
3: private T y;
4:
5: public T getX() {
6: return x;
7: }
8: public void setX(T x) {
9: this.x = x;
10: }
11: public T getY() {
12: return y;
13: }
14: public void setY(T y) {
15: this.y = y;
16: }
17: }
1: public class GenericsTest {
2:
3: /**
4: * @param args
5: */
6: public static void main(String[] args) {
7: // TODO Auto-generated method stub
8: MyGenericClass<String> mgc = new MyGenericClass<String>();
9: mgc.setX("x");
10: mgc.setY("y");
11: System.out.println(mgc.getX());
12: System.out.println(mgc.getY());
13: }
14: }
泛型方法的定义
当类中不同方法操作的数据类型不同时,我们可以将泛型定义在方法上
1: public class MyGenericClass<T> {
2: private T x;
3: private T y;
4:
5: public T getX() {
6: return x;
7: }
8: public void setX(T x) {
9: this.x = x;
10: }
11: public T getY() {
12: return y;
13: }
14: public void setY(T y) {
15: this.y = y;
16: }
17:
18: public static <E extends Comparable<E>> E max(E a,E b){
19: if(a.compareTo(b) > 0)
20: return a;
21: else
22: return b;
23: }
24: }
1: public class GenericsTest {
2:
3: /**
4: * @param args
5: */
6: public static void main(String[] args) {
7: // TODO Auto-generated method stub
8: MyGenericClass<String> mgc = new MyGenericClass<String>();
9: mgc.setX("x");
10: mgc.setY("y");
11: System.out.println(mgc.getX());
12: System.out.println(mgc.getY());
13:
14: //调用静态方法max
15: String max = MyGenericClass.max("123", "456");
16: System.out.println(max);
17: }
18: }
上面的例子中,max静态方法定义的泛型E与泛型类中定义的泛型T不同,只在max方法上适用,至于extends范围限定,等下会提到
?通配符
?通配符不管是在泛型的定义或者使用上,都应用非常广泛
它表示不确定的泛型类型
例如:
1: public class GenericsTest2 {
2:
3: /**
4: * @param args
5: */
6: public static void main(String[] args) {
7: // TODO Auto-generated method stub
8: print(new ArrayList<String>());
9: }
10: private static void print(Collection<?> col){
11: Iterator<?> it = col.iterator();
12: while(it.hasNext()){
13: System.out.println(it.next());
14: }
15: }
16: }
这样的代码,当我们不确定要传入的实际类型的时候,可以用?作为占位符,表示该类型不确定,那么它的弊端也是显而易见的,就是不能调用类型的特有方法
比如
System.out.println(it.next().length())//这样是不允许的,因为传入的参数类型可能是数组类型
? i = it.next();//这样是不允许的
这也就引出了?与T在定义时的区别
?与T都表示不确定的参数类型,他们有同样的弊端,就是不能调用类型的特有方法
但是T至少可以用作引用,比如 T i = it.next();,这样是可以的,但是定义?的时候就不行了
泛型限定
<? extends E>:可以接收E类型或者E的子类型,即设置上限
<? super E>:可以接收E类型或者E的父类型,即设置下限
1: public class GenericsTest2 {
2:
3: /**
4: * @param args
5: */
6: public static void main(String[] args) {
7:
8: //String并不是Number的子类,所以并不能作为参数传递过去
9: print(new ArrayList<String>());
10: }
11: private static void print(Collection<? extends Number> col){
12: Iterator<?> it = col.iterator();
13: while(it.hasNext()){
14: System.out.println(it.next());
15: }
16: }
17: }
上面的例子中,由于print方法定义了?通配符的范围,规定该泛型必须为Number或者Number的子类,String并不是Number的子类,所以并不能作为参数传递过去
通过反射获取泛型的实际类型参数
由于泛型的类型擦除机制,我们在运行过程中是无法从集合类本身获取类型参数的,所以只能从带有这些泛型类参数的方法中发射获取
1: public class GetParameterdTypeByReflect {
2:
3: /**
4: * @param args
5: * @throws NoSuchMethodException
6: * @throws SecurityException
7: */
8: public static void main(String[] args) throws SecurityException, NoSuchMethodException {
9:
10: //获取参数列表中带有泛型类的方法对象
11: Method method = GetParameterdTypeByReflect.class.getMethod("applyMethod", ArrayList.class,Collection.class);
12:
13: //得到方法参数中带有泛型的参数类型
14: Type[] types = method.getGenericParameterTypes();
15:
16: //遍历这些类型
17: for(Type type : types){
18: //将Type转为ParameterizedType(参数化类型)
19: ParameterizedType pType = (ParameterizedType)type;
20: //打印参数化类型名称
21: System.out.println(pType);
22: //打印参数化类型的原始类型
23: System.out.println(pType.getRawType());
24: //打印参数化类型的实际类型参数列表
25: for(int i = 0 ; i < pType.getActualTypeArguments().length ; i ++){
26: System.out.println(pType.getActualTypeArguments()[i]);
27: }
28: }
29: }
30:
31: public static void applyMethod(ArrayList<String> arrayList,Collection<?> col){
32:
33: }
34: }
JDK1.5新特性(六)……Generics的更多相关文章
- JDK1.5新特性,基础类库篇,集合框架(Collections)
集合框架在JDK1.5中增强特性如下: 一. 新语言特性的增强 泛型(Generics)- 增加了集合框架在编译时段的元素类型检查,节省了遍历元素时类型转换代码量. For-Loop循环(Enhanc ...
- JDK1.8新特性——使用新的方式遍历集合
JDK1.8新特性——使用新的方式遍历集合 摘要:本文主要学习了在JDK1.8中新增的遍历集合的方式. 遍历List 方法: default void forEach(Consumer<? su ...
- JDK1.7新特性
jdk1.7新特性 1 对集合类的语言支持: 2 自动资源管理: 3 改进的通用实例创建类型推断: 4 数字字面量下划线支持: 5 switch中使用string: 6 二进制字面量: 7 简化可变参 ...
- jdk1.6新特性
1.Web服务元数据 Java 里的Web服务元数据跟微软的方案基本没有语义上的区别,自从JDK5添加了元数据功能(Annotation)之后,SUN几乎重构了整个J2EE体 系, 由于变化很大,干脆 ...
- JDK1.8 新特性
jdk1.8新特性知识点: Lambda表达式 函数式接口 *方法引用和构造器调用 Stream API 接口中的默认方法和静态方法 新时间日期API https://blog.csdn.net/qq ...
- JDK1.6新特性,WebService强化
Web service是一个平台独立的,松耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML标准来描述.发布.发现.协调和配置这些应用程序,用于开发分布式的互操作的应用程序. Web ...
- JDK1.7新特性(2):异常和可变长参数处理
异常 jdk1.7对try--catch--finally的异常处理模式进行了增强,下面我们依次来看增强的方面. 1. 为了防止异常覆盖,给Throwable类增加了addSuppressed方法,可 ...
- jdk1.8新特性应用之Iterable
我们继续看lambda表达式的应用: public void urlExcuAspect(RpcController controller, Message request, RpcCallback ...
- jdk1.8新特性之方法引用
方法引用其实就是方法调用,符号是两个冒号::来表示,左边是对象或类,右边是方法.它其实就是lambda表达式的进一步简化.如果不使用lambda表达式,那么也就没必要用方法引用了.啥是lambda,参 ...
随机推荐
- 指针 取地址& 解引用 *
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtAAAACNCAIAAAARutrLAAAgAElEQVR4nOydd3wcxd3/R13uvdsUY2
- 在SpringMVC利用MockMvc进行单元测试
spring在线文档:https://docs.spring.io/spring/docs/current/javadoc-api/index.html?index-files/index-13.ht ...
- [笨木头FireFly01]入门篇1·最简单的服务端和客户端连接
原地址:http://www.9miao.com/question-15-53938.html 最近一直在写游戏,几乎没有来写教程了,打算放慢一下脚步,学学新东西.那为嘛我要学FireFly呢? 之前 ...
- Eclipse 插件开发 —— 深入理解查找(Search)功能及其扩展点
引言 查找功能是计算机语言开发环境 / 平台的一个非常重要的特性.Eclipse 也不例外,它提供了丰富的查找功能(用户可以输入正则表达式或任意字符串,指定查找范围和匹配选项等等),并且提供了简单易用 ...
- NSString的常用方法
创建一个新字符串并将其设置为 path 指定的文件的内容,使用字符编码enc,在error上返回错误 + (id)stringWithContentsOfURL:(NSURL *)url encodi ...
- 核心思想:早胜过一切,张小龙的Foxmail居然可以卖1200万
现在谁都可以做一个类似的软件,但是市场已经成熟了,满大街都是,也就没有人会来收购你的软件了.
- 《RabbitMQ in action》
Producers create messages and publish (send) them to a broker server (RabbitMQ).What’s a message? A ...
- 如何完全卸载VS2010
1.首先用360卸载,当卸载完成后,提示有残余的话,就强力清除 2,接着,下载IobitUninstaller工具 3.按照下面进行卸载 1.Microsoft .NET Framework 4 框架 ...
- jQgrid问题总结
最近一段时间一直在使用jqgrid这个免费的插件,网上的资料也比较多.比较全,但是这里还是整理几个自己在开发过程中遇到的小问题. 1.自动换行 一行数据过多需要自动根据内容换行时,如果遇到在表格中的汉 ...
- SGU185 - Two Shortest
原题地址:http://acm.sgu.ru/problem.php?contest=0&problem=185 题目大意:给出一个无向图,求出从 1 到 n 的两条没有相同边的最短路径(允许 ...