Java泛型用于解决API设计者在设计底层程序时不知道应用程序开发者会具体使用哪些类型的应用场景。比如Java集合框架,API设计者不知道应用程序设计者在调用集合API时会往里面加入什么类型,JDK5通过引入泛型很好地解决了这个问题。
写在前面的—泛型程序主要用于库程序的开发,但是在应用程序开发中也会用到,作为一名码农,虽然用到泛型的场合比较少,但是还是有必要弄懂,以防工作卡卡壳。
1 泛型类的设计
Java通过使用一种称为类型参数的术语来进行泛型设计。类型参数在泛型程序设计中可以看做一种类型。比如如下代码:
public class Generic<T> {
private T t;
public Generic(T t) {
this.t = t;
}
public T getT(){
rerturn t;
}
public void setT(T t) {
this.t = t;
}
}
在上述代码中,字母T(也可以是其它字母)就是类型参数,T由调用Generic的程序给定,T可以是任意的非 泛型类型。实例化一个泛型类型的代码如下:
Generic<String> generic = new Generic<String>();
在Java SE7及后续版本中,可以上省略构造函数的类型参数,具体代码如下:
Generic<String> generic = new Generic<>();
2 泛型方法
有一点需要注意的是泛型方法并不一定要求存在于泛型类中,在普通类型中也能写泛型方法。泛型方法的设计如下:
public <T> T f(T t){
return t;
}
类型变量放在访问修饰符和返回类型的前面。
3 类型变量的限定
有时候 我们不希望类型变量面向的是任意的类型,比如有一个Family泛型类,我们希望Family里面的类型只有People及其子类,此时就需要对类型变量进行限定。
Java 使用extends限定类型变量,下面的代码展示了在泛型方法中限定类型变量
public <T extends Number> T f(T t) {
...
}
可以使用具体的类或者是接口来限定类型变量,也可以对类型变量使用多个限定,其中限定类型使用”&”分隔,而多个类型变量则使用”,”分隔。限定中最多只有一个类,并且必须是第一个,其余的只能是接口类型。
4 类型擦除
实际上,在JVM中是不存在泛型类型对象的,也就是说JVM中所有的对象都是普通java对象。泛型类型经过编译器编译后会产生一个原始类型,原始类型的名字就是删除类型参数后的泛型类型名。实现类型擦除以及调用泛型时的墙纸类型转换是编译器完成的。
类型擦除在泛型继承时会出现一些问题,比如如下的代码:
public GenericEx extends Generic<String> {
public void setT(String s) {
System.out.println("Hello: " + s);
}
}
在应用程序中有如下调用:
Generic<String> generic = new GenericEx<>("World");
generic.setT("how are you!");
generic 的类型是Genenric<String>
,但是指向的是对象是GenericEx。根据Java多态的特性,generic调用setT()方法应该是GenericEx<String>
对象的方法。但是Generic类型经过类型擦除后,setT(T t)
方法变成set(Object t)
, 因为String是Object的子类,generic应该调用Generic中的setT(Object t)方法,这样jvm不知道应该调用哪个方法,导致了类型擦除与多态冲突。为了解决这个问题,Java设计者在编译器编译时引入了一个桥接器
public void setT(Object t) {
setT((String) t);
}
有了这个桥接器后,程序仍然调调用Generic中的setT(Object t)方法,但是这个方法转而调用set(String t)方法,满足多态的调用。
5 通配符
在泛型程序设计中,使用”?”表示通配符。通配符表示任何泛型类型。通配符是为了解决泛型类型的继承问题而引入的,具体可以参考这篇文章:
http://blog.csdn.net/s10461/article/details/53941091
References
《Core Java》第9版
http://blog.csdn.net/s10461/article/details/53941091
http://www.cnblogs.com/lucky_dai/p/5589317.html