初识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的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...
随机推荐
- html5新增标签及兼容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 一次dell R420 电源故障引发的“血案”
说“血案”有写夸张了,其实是也就熬了一夜的通宵,做运维的伤不起啊,作为一名运维工程师,像这种服务器突发故障半夜起床的情况属于家常便饭,见怪不怪了,开始说正事: 前几天半夜12点左右,收到服务器宕机的消 ...
- java封装好处和原则
/*封装好处 隐藏实际细节,提供公共的访问方式 提高了代码的复用性 提高安全性 封装原则 将不需要对外提供的内容都隐藏起来 把属性隐藏,提供公共方法对其访问.*/
- 一款全兼容的播放器 videojs
[官网]http://www.videojs.com/ videojs就提供了这样一套解决方案,他是一个兼容HTML5的视频播放工具,早期版本兼容所有浏览器,方法是:提供三个后缀名的视频,并在不支持h ...
- ABAP 分货日报表
*&---------------------------------------------------------------------* *& Report ZSDR031 ...
- Java 集合类 TreeSet、TreeMap
TreeMap和TreeSet的异同: 相同点: TreeMap和TreeSet都是有序的集合,也就是说他们存储的值都是拍好序的. TreeMap和TreeSet都是非同步集合,因此他们不能在多线程之 ...
- javaSE基础02
javaSE基础02 一.javac命令和java命令做什么事情? javac:负责编译,当执行javac时,会启动java的编译程序,对指定扩展名的.java文件进行编译,生成了jvm可以识别的字节 ...
- 使用原生ajax处理json组成的数组
和前一篇文章一样,直接上代码了,只是做个记录. 数据的提供页面,tigong.php <?php header("content-type:text/html;charset=utf- ...
- 图像处理中的matlab使用
图像的矩阵表示 类和图像类型 虽然使用的是整数坐标, 但 MATLAB 中的像素值(亮度)并未限制为整数. 表 1-1 列出了 MATLAB 和图像处理工具箱为描述像素值而支持的各种类. 表中的前 8 ...
- 如何在插件中添加Actor类
只要按照BlueprintFunctionLibrary的那个模板做就好了,把用编辑器生成的代码复制到插件对应的目录 之后需要注意做以下修改 1.H文件中的 GENERATED_UCLASS_BODY ...