Java泛型、泛型类、泛型方法详解
一、泛型
泛型是Java 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java泛型被引入的好处是安全简单。在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
简单来说就是引入泛型,可以在编译阶段就发现类型转换错误,从而实现安全。
注: Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
如在代码中定义的List<Integer>和List<String>等类型,在编译后都会编程List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。
二、泛型类
通过一个demo来了解泛型类的用途
从下面的demo中,我们可以分析出以下几点:
1.泛型类的类型和类中的方法的返回值,参数类型无关
2.泛型类大多用于在实例化时指定类为类型,从而指定方法中参数该用什么类型,在编译阶段就找出类型错误
3.用于灵活地指定不同类的数据操作
可参考ArrayList,LinkedList等容器类进行深入理解
public class Genericity<E> {
public String get(Integer id){
return null;
}
public Integer get2(String id){
return null;
}
public void get3(E id){
System.out.println("get3 ....");
}
public static void main(String[] args) {
Genericity<String> g = new Genericity<String>();
String a="5";
g.get3(5);//编译出错,5不为String类型
g.get3(a);//不报错,因为参数的类型和泛型类相同
Genericity<Integer> gg = new Genericity<Integer>();
gg.get3(5);//不报错,因为参数的类型和泛型类相同
gg.get3(a);//编译出错,a不为Integer类型
}
}
三、泛型方法
泛型方法相对于泛型类要稍微复杂一点,首先我们必须清楚怎么用才是泛型方法。
public class Genericity<E> {
private E key;
public Genericity() {
this.key = key;
}
public Genericity(E key) {
this.key = key;
}
//这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
//所以在这个方法中才可以继续使用 E 这个泛型。
public E getKey(){
return key;
}
/**
* 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
* 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
*/
public T setKey(T key){
this.key = key;
}
/**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <E> E showKeyName(Genericity<E> container){
System.out.println("container key :" + container.getKey());
E test = container.getKey();
return test;
}
//这也不是一个泛型方法,这就是一个普通的方法,只是使用了Genericity<Number>这个泛型类做形参而已。
public void showKeyValue1(Genericity<Number> obj){
}
//这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
public void showKeyValue2(Genericity<?> obj){
}
/**
* 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
* 所以这也不是一个正确的泛型方法声明。
*/
public void showkey(E genericObj){
}
一个真正的泛型方法。
- 首先在public与返回值之间的必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
- 这个T可以出现在这个泛型方法的任意位置.
- 泛型的数量也可以为任意多个
泛型方法的使用:
public <E> E getObject(Class<E> c) throws InstantiationException, IllegalAccessException{
E e = c.newInstance();
return e;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Genericity g = new Genericity();
Object obj=g.getObject(Class.forName("Genercity"));
}
这样我们就可以通过传入不同的类型来通过泛型方法获取不同类型的Object,而不用去每次都将该类型new出来。
关于泛型的上下界问题----super 和 extends,欢迎参考下列博客
泛型的上下界问题-------super和extends