




已阅读5页,还剩1页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Java范型浅析收藏新一篇: 枚举入门 | 旧一篇: 几个著名java开源缓存系统的介绍 从jdk1.5开始,Java中开始支持范型了。范型是一个很有用的编程工具,给我们带来了极大的灵活性。在看了java核心编程之后,我小有收获,写出来与大家分享。 所谓范型,我的感觉就是,不用考虑对象的具体类型,就可以对对象进行一定的操作,对任何对象都能进行同样的操作。这就是灵活性之所在。但是,正是因为没有考虑对象的具体类型,因此一般情况下不可以使用对象自带的接口函数,因为不同的对象所携带的接口函数不一样,你使用了对象A的接口函数,万一别人将一个对象B传给范型,那么程序就会出现错误,这就是范型的局限性。 所以说,范型的最佳用途,就是用于实现容器类,实现一个通用的容器。该容器可以存储对象,也可以取出对象,而不用考虑对象的具体类型。因此,在学习范型的时候,一定要了解这一点,你不能指望范型是万能的,要充分考虑到范型的局限性。下面我们来探讨一下范型的原理以及高级应用。首先给出一个范型类:public class Pair public Pair() first = null; second = null; public Pair(T first, T second) this.first = first; this.second = second; public T getFirst() return first; public T getSecond() return second; public void setFirst(T newValue) first = newValue; public void setSecond(T newValue) second = newValue; private T first; private T second; 我们看到,上述Pair类是一个容器类(我会多次强调,范型天生就是为了容器类的方便实现),容纳了2个数据,但这2个数据类型是不确定的,用范型T来表示。关于范型类如何使用,那是最基本的内容,在此就不讨论了。 下面我们来讨论一下Java中范型类的实现原理。在java中,范型是在编译器中实现的,而不是在虚拟机中实现的,虚拟机对范型一无所知。因此,编译器一定要把范型类修改为普通类,才能够在虚拟机中执行。在java中,这种技术称之为“擦除”,也就是用Object类型替换范型。上述代码经过擦除后就变成如下形式: Comparable来替换所有范型),当需要用到其他约束中定义的方法的时候,通过插入强制转化代码来实现。在此就不给出具体的例子了。 下面我们来看看最后一个知识点,定义一个函数,该函数接受一个范型类作为参数。首先让我们来看一个最简单的情况,参数是一个实例化的范型类: l.get(0)是合法的,因为参数是整型而不是范型;l.add(x)就不合法,因为add函数的参数是范型。但是定义一个范型方法还是有一定灵活性的,如果传入的数据也是范型,编译器还是认可的,因为范型对范型,类型安全是可以保证的。 从上述代码可以看出,定义一个范型方法要比Wildcard稍微灵活一些,可以往链表中添加T类型的对象,而Wildcard中是不允许往链表中添加任何类型的对象的。那么我们还要Wildcard干什么呢?Wildcard还是有他存在的意义的,那就是,Wildcard支持另外一个关键字super,而范型方法不支持super关键字。换句话说,如果你要实现这样的功能:“传入的参数应该是指定类的父类”,范型方法就无能为力了,只能依靠Wildcard来实现。代码如下: l.add(Integer)x);),编译器不就不报错了吗?确实,经过强制转化后,编译器确实没意见了。不过这种强制转化有可能带来运行时错误。因为你传入的实参,其元素类型是Integer的父类,比如是Number。那么,存储在该链表中的第一个数据,很有可能是Double或其他类型的,这是合法的。那么你取出的第一个元素x也会是Double类型。那么你把一个Double类型强制转化为Integer类型,显然是一个运行时错误。 难道“把取出的元素再插入到链表中”这样一个功能就实现不了吗?当然可以,不过不能直接实现,要借助范型函数的帮忙,因为在范型函数中,刚刚取出的元素再存回去是不成问题的。定义这样一个范型函数,我们称之为帮助函数。代码如下: public class Pair public Pair(Object first, Object second) this.first = first; this.second = second; public Object getFirst() return first; public Object getSecond() return second; public void setFirst(Object newValue) first = newValue; public void setSecond(Object newValue) second = newValue; private Object first; private Object second; 大家可以看到,这是一个普通类,所有的范型都被替换为Object类型,他被称之为原生类。每当你用一个具体类去实例化该范型时,编译器都会在原生类的基础上,通过强制约束和在需要的地方添加强制转换代码来满足需求,但是不会生成更多的具体的类(这一点和c+完全不同)。我们来举例说明这一点: Pair buddies = new Pair(); /在上述原生代码中,此处参数类型是Object,理论上可以接纳各种类型,但编译器通过强制约束/你只能在此使用Employee(及子类)类型的参数,其他类型编译器一律报错buddies.setFirst(new Employee(张三); /在上述原生代码中,getFirst()的返回值是一个Object类型,是不可以直接赋给类型为Employee的buddy的/但编译器在此做了手脚,添加了强制转化代码,实际代码应该是Employee buddy = (Employee)buddies.getFirst();/这样就合法了。但编译器做过手脚的代码你是看不到的,他是以字节码的形式完成的。Employee buddy = buddies.getFirst(); 下面我们再来考察一个更复杂的情况,如果我们的Pair类要保证第二个属性一定要大于第一个属性,该如何做?这就涉及到两个属性的比较,但是这2个属性类型未知,可以比较吗?我们前面也讲过,一般情况下不要涉及类型的具体信息。但是现在要比较2个属性,不得不涉及类型的具体信息了。Java还是考虑到了这一点,那就是,范型类可以继承自某一个父类,或者实现某个接口,或者同时继承父类并且实现接口。这样的话,就可以对类型调用父类或接口中定义的方法了。代码如下: public class Pair public boolean setSecond(T newValue) boolean flag = false; If(newVpareTo(first)0) second = newValue; flag = true; return flag; private T first; private T second; 我们看到,上面的范型T被我们添加了一个约束条件,那就是他必须实现Comparable接口,这样的话,我们就可以对范型T使用接口中定义的方法了,也就可以实现2个元素大小的比较。有人可能要问了,实现一个接口不是用implements吗?上面怎么用extends呢?为了简化范型的设计,无论是继承类还是实现接口,一律使用extends关键字。这是规定,没办法,记住就行了。若同时添加多个约束,各个约束之间用“&”分隔,比如:public class Pair。那么编译器是如何处理这种情况呢?前面讲过,范型类最终都会被转化为原生类。在前面没有添加约束的时候,编译器将范型通通替换为Object;而增加了约束之后,通通用第一个约束来替换范型(上面的代码就会用 public static void test(ArrayList l) l.add(new Integer(2); 上述代码中,形参list的元素被实例化为Number类型。在使用该函数的时候我们能不能传入一个元素为Integer的list呢?看看下面代码合法吗? ArrayList l = new ArrayList(); test(l); /此处编译器会报错! 答案上面已经给出了:不行!对于这种形参,实参的类型必须和他完全一致,即也应该是一个元素为Number的list才可以,其他的实参一律不行。这是为什么呢?Integer不是Number的子类吗?子类的对象传递给父类的引用,不可以吗?这里我们就要注意了,Integer确实是Number的子类,但是,ArrayList并不是ArrayList的子类,二者之间没有任何的继承关系!因此这样传递参数是不允许的。如果允许的话,会出现什么问题吗?当然会,我们对test函数重新定义一下: public static void test(ArrayList l) l.add(new Float(2); 大家可以看到,在函数内部,我们把Float类型的元素插入到链表中。因为链表是Number类型,这条语句没问题。但是,如果实参是一个Integer类型的链表,他能存储Float类型的数据吗?显然不能,这样就会造成运行时错误。于是,编译器干脆就不允许进行这样的传递。 通过分析我们看到,出错的可能性只有一个:在向容器类添加内容的时候可能造成类型不匹配。那么有些人可能会有这种要求:“我保证一定不对容器添加内容,我非常希望能够将一个Integer类(Number类的子类)组成的链表传递进来”。Sun的那帮大牛们当然会考虑到这种诉求,这样的功能是可以实现的,并且还有两种方式呢,看下面代码: / 1.在定义方法的时候使用Wildcard(也就是下述代码中的问号)。 public static void test1(ArrayList l) Integer n = new Integer(45); Number x = l.get(0); /从链表中取数据是允许的 l.add(n); /错误!往链表里面插入数据是被编译器严格禁止的! / 2.定义一个范型方法。代码如下: public static void test2(ArrayList l) Number n = l.get(0); T d = l.get(0); l.add(d); /与上面的方法相比,插入一个范型数据是被允许的,相对灵活一些 l.add(n); /错误!只可以插入范型数据,绝不可插入具体类型数据。 按照上述代码的写法,只要我们对形参添加了一定的约束条件,那么我们在传递实参的时候,对实参的严格约束就会降低一些。上述代码都指定了一个类Number,并用了extends关键字,因此,在传递实参的时候,凡是从Number继承的类组成的链表,均可以传递进去。但上面代码的注释中也说的很清楚,为了不出现运行时错误,编译器会对你调用的方法做严格的限制:凡是参数为范型的方法,一律不需调用! public static void test5(ArrayList l) Integer n = new Integer(45); l.add(n); /与上面使用extends关键字相反,往链表里面插入指定类型的数据是被允许的。 Object x = l.get(0); /从链表里取出一个数据仍然是被允许的,不过要赋值给Object对象。 l.add(x); /错误!将刚刚取出的数据再次插入链表是不被允许的。 这种实现方式的特点我们前面已经说过了,就是对实参的限制更改为:必须是指定类型的父类。这里我们指定了Integer类,那么实参链表的元素类型,必须是Number类及其父类。下面我们重点讨论一下上述代码的第四条语句,为什么将刚刚取出的数据再次插入链表不被允许?道理很简单,刚刚取出的数据被保存在一个Object类型的引用中,而链表的add方法只能接受指定类型Integer及其子类,类型不匹配当然不行。有些人可能立刻会说,我将他强制转化为Integer类(即 /帮助函数 public static void helperTest5(ArrayList l, int index) T temp = l.get(index); l.add(temp); /主功能函数 public static void test5(ArrayList l) Integer n = new Integer(45); l.add(n); helperTest5(l, 0); /通过帮助类,将指定的元素取出后再插回去。 上述两个函数结合的原理就是:利用Wildcard的super关键字来限制参数的类型(范型函数不支持super,要是支持的话就不用这么麻烦了),然后通过范型函数来完成取出数据的再存储。 以上就是我学习范型的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 九年级物理阅读拓展训练计划
- 地面砖、石材铺贴施工安全难点及解决措施
- 部编版高中语文必修下册教学反馈改进计划
- 2025年疫苗采购与预防接种工作计划
- 麻醉精神药品管理人员核查职责
- “五育”推动学生心理健康教育计划
- 小学一年级语文分级教学计划
- 部编版一年级道德与法治下册家庭配合教学计划
- 2024-2025学年校园关心下一代活动计划
- 2025春小学语文教研组课堂互动设计计划
- 城市水工程概论
- 撤销冒名登记(备案)申请表
- 减肥总结:如何制定有效的减肥计划PPT
- 眼视光医学专业综合概述
- 易制毒化学品安全管理培训
- 八少八素图形推理测试真题
- 股东风险协议书
- 2023-2024学年广东省潮州市小学语文六年级期末自测模拟考试题附参考答案和详细解析
- 《供应链协同的研究文献综述》
- 鼻窦导航般阅片改进版
- 中医病证诊断疗效标准
评论
0/150
提交评论