初识Java泛型以及桥接方法
泛型的由来
在编写程序时,可能会有这样的需求:
容器类,比如java中常见的list等。为了使容器可以保存多种类型的数据,需要编写多种容器类,每一个容器类中规定好了可以操作的数据类型。
此时可能会有IntList、LongList、StringList、XXXList等多个List容器类,但是容器类内部的各个方法,除了add、remove、get方法操作的数据类型不同意外,几乎全部相同。为了方便起见,可以使用ObjectList,但是在get的数据是Object类型,需要进行强制类型转换,这就会造成一些程序运行时的隐患。
通过查询网络资料时,发现这个需求对于泛型的需要最迫切。于是在jdk1.5(2004年)中,java加入了泛型。
什么是泛型
泛型:参数化类型。将类类型,作为参数,进行传递。
泛型参数,可以为多个,使用尖括号括起来。
比如,通过实例化List容器时,指定容器类类型String,实现了String容器。
List<String> list= new LinkedList<String>();
list.add("1");//没有编译错误
list.add(1);//编译错误
如何实现泛型
在java中,可实现泛型接口、泛型类、泛型方法。
1、泛型接口:常见的List接口,Map接口,都是泛型接口。
举例:
public interface Test<T>{
public void add(T element);
public T get(int index);
}
2、泛型类:LinkedList、ArrayList、HashMap、TreeMap等都是泛型类,并且他们实现了泛型接口;
举例:
public class Target<T, E> implements Test<T>{
private Object[] list=new Object[100];
private int index=0;
public void add(T element){
list[index++]=element;
}
public T get(int index){
return (T)list[index];
}
}
3、泛型方法:同泛型接口和泛型类的定义稍微有些不同。
举例:
public <T> T add( Class<T> clz){
T instance=clz.newInstance();
return instance
}
在定义泛型接口、类、方法时,可以添加多个泛型形参,一般使用大写字母表示,并用逗号分隔,泛型定义处,使用尖括号将所有泛型形参括起来。
泛型接口、类的泛型形参,放置在接口或类名的后面。
泛型方法的泛型形参,放置在方法返回类型的前面即可。
注意,java的泛型属于code share,只是在编译时使用,用于检查传递给泛型类、方法的数据类型是否符合泛型定义,泛型类和方法中的泛型会在编译成功后擦出,成为Object类型,编译器并会在泛型数据操作时,自动添加类型转换。由于编译期间保证了泛型类型的正确使用,因此自动添加的类型转换是安全的。
举例:
使用List容器,调用其方法
public class Main{
public static void main(String[] args){
List<Long> ttt=new LinkedList<Long>();
tt.add(1L);
Long value=tt.get(0);
}
}
在编译后的class文件,可翻译为:
public class Main{
public static void main(String[] args){
List ttt=new LinkedList();
tt.add(1L);
Long value=(Long)tt.get(0);
}
}
编译时,编译器会检查凡是使用泛型类实例的地方,是不是按照泛型的规定,进行传参的。
比如ttt.add(1L),编译器会检查传入的参数是不是Long型;还有tt.get(0),编译器会自动加上类型转换。最终看到编译后,泛型类的泛型被擦出了。
类型擦出导致,以下输出会为true,因为泛型类不会因为不同的泛型类型,导致生成不同的类。
List<String> list1=new LinkedList<String>();
List<Integer> list2=new LinkedList<Integer>();
System.out.println(list2.getClass()==list1.getClass());//输出为true
泛型定义时的约束
定义泛型形参时,可以使用extends限制泛型的范围。
比如使用<T extends K>,表示T必须是类K的子类,或者接口K的实现类。
比如使用<T super K>,表示T必须是类K的父类,或类K实现的接口。
注意:extends并不表示类的继承含义,只是表示泛型的范围关系。
注意:extends中可以指定多个范围,实行泛型类型检查约束时,会以最左边的为准。
java泛型实现的原理
在C++中也有泛型的概念,但java的泛型实现原理同C++是不一样的。
泛型实现原理有两种:
1、为使用的泛型类,单独生成一份非泛型的具体类。
比如List<String> list=new ArrayList<String>,在程序编译时,会单独生成一份StringArrayList类,并且类里面所有的操作元素都是String类。
2、采用擦出机制,泛型接口或类中,所有使用泛型形参的地方,全部擦除,替换为Object类型(java中所有类的父类)。所有相同泛型类的实例共享使用泛型类的代码。在泛型类的实例进行数据操作的地方(泛型类外部,自定义的程序部分),由编译器检查操作的参数是否为泛型类实例定义时的类类型,必要时自动添加强制类型转换。
在擦除时,如果<T extends XXClass>,T擦出后会变为XXClass。
如果<T extends AClass,BClass>,T擦出后,类型为AClass,以最左边的类型为最终类型,这点要注意。
桥接方法
首先举一个例子。
public class A<T>{
public T get(T a){
//a进行一些操作
return a;
}
}
public class B extends A<String>{
@override
public String get(String a){
//a进行一些操作
return a;
}
}
由于类型擦出机制的存在,按理说编译后的文件在翻译为java应如下所示
public class A{
public Object get(Object a){
//a进行一些操作
return a;
}
}
public class B extends A{
@override
public String get(String a){
//a进行一些操作
return a;
}
}
@override意味着B对父类A中的get方法进行了重写,但是依上面的程序来看,只是重载,依然可以执行父类的方法,这和期望是不附的,也不符合java继承、多态的特性。
注:子类重写父类方法,只要此实例的实际类型是子类类型了(不是引用类型),便不能调用父类的方法,这是语言的多态特性。
为了解决这个问题,java在编译期间加入了桥接方法。编译后再翻译为java原文件,应如下所示:
public class A{
public Object get(Object a){
//a进行一些操作
return a;
}
}
public class B extends A{
@override
public String get(String a){
//a进行一些操作
return a;
}
public Object get(Object a){
return get((String)a)
}
}
类B中的第二个方法,即为桥接方法。桥接方法重写了父类相同的方法,并且桥接方法中,最终调用了期望的重写方法,并且桥接方法在调用目的方法时,参数被强制转换为指定的泛型类型。
桥接方法测试:
A a=new B();
a.get(new Object());
由于A未声明泛型类型,编译期间会警告存在类型转换错误。
运行期间,还真抛出了类型转换错误,Object to String。
实例a实际类型为B,在调用a.get方法时最终调用了B类中的桥接方法,保证了传入参数为子类继承父类使用的泛型类型。
初识Java泛型以及桥接方法的更多相关文章
- Java系列:关于Java中的桥接方法
这两天在看<Java核心技术 卷1>的泛型相关章节,其中说到了在泛型子类中override父类的泛型方法时,编译器会自动生成一个桥接方法,这块有点看不明白. 书上的例子代码如下: publ ...
- java中的桥接方法
本文转载自java中什么是bridge method(桥接方法) 导语 在看spring-mvc的源码的时候,看到在解析handler方法时,有关于获取桥接方法代码,不明白什么是桥接方法,经过查找资料 ...
- 使用java泛型设计通用方法
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...
- 初识java泛型
1 协变数组类型(covariant array type) 数组的协变性: if A IS-A B then A[] IS-A B[] 也就是说,java中的数组兼容,一个类型的数组兼容他的子类类型 ...
- 3分钟快速搞懂Java的桥接方法
什么是桥接方法? Java中的桥接方法(Bridge Method)是一种为了实现某些Java语言特性而由编译器自动生成的方法. 我们可以通过Method类的isBridge方法来判断一个方法是否是桥 ...
- java 泛型的类型擦除与桥方法
泛型类 --代码参考:java核心技术 卷1 第十版 public class Pair<T> { private T first; private T second; //构造器 pub ...
- java泛型基础
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法. Ja ...
- Java泛型-内部原理: 类型擦除以及类型擦除带来的问题
一:Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除.Java的泛型基本上都是在编译 ...
- java泛型 8 泛型的内部原理:类型擦除以及类型擦除带来的问题
参考:java核心技术 一.Java泛型的实现方法:类型擦除 前面已经说了,Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...
随机推荐
- div内填内容
<div contenteditable="true">可以编辑里面的内容</div> 这样就可以使div想textarea一样 可以加入自动换行与over ...
- OC编程之道-创建对象之单例模式
一 何为单例singleton模式?(what) 保证一个类只有一个实例,并提供一个访问它的全局访问点. 二 何时使用单例模式?(where) 1类只能有一个实例,而且必须从一个为人熟知的访问点对其访 ...
- (2016弱校联盟十一专场10.3) A.Best Matched Pair
题目链接 #include<cstdio> #include<cstring> #include<algorithm> #include<stack> ...
- Android笔记:多线程
定义线程的两个方法: 1. class MyThread extends Thread { public void run() {// 处理具体的逻辑 } } new MyThread().start ...
- 使用eclipse+fiddler+微信web开发者工具调试本地微信页面
前面已经说了调试服务器上的微信页面,放链接:http://www.cnblogs.com/Gabriel-Wei/p/5977850.html 还有fiddler调试链接:http://www.cnb ...
- struts2+jsp+hiberbate 双重遍历
今天弄了个双重遍历,由于自己水平有限,做了好久才搞定. 例子如下:也不算是例子,简要代码吧 action中有属性: private List<Contents> content; 其中co ...
- 从Eclipse迁移到Android Studio碰到的问题记录
背景: 1. 一直在做.NET的开发,工作之余,学习了一下Android开发,写了一些Demo,当时用的Eclipse开发工具:这两天,刚好项目不是很忙,就打算把之前写的Demo,迁移到Android ...
- Python 打包和发布方法汇总
以下主要Python打包汇总,作为学习备份: 一.使用python内部基础工具包Distutils打包程序 1. 在打包之前需要做的就是配置好安装脚本,一般为setup.py文件: 示例(setup. ...
- UVALive5031 Graph and Queries(Treap)
反向操作,先求出最终状态,再反向操作. 然后就是Treap 的合并,求第K大值. #include<cstdio> #include<iostream> #include< ...
- 理解WebService SOAP WSDL
WebServices简介 先给出一个概念 SOA ,即Service Oriented Architecture ,中文一般理解为面向服务的架构, 既然说是一种架构的话,所以一般认为 SOA 是包含 ...