JAVA泛型
1.概述
泛型:即“参数化类型”。将类型由原来的具体类型参数化,类似于方法中的变量参数,此时类型同样定义为参数形式,只有在调用/运行时才传入具体的类型。
泛型的本质:为了参数化类型,即在不创建新的类型的情况下,通过反省制定的不同类型来控制形参具体显限制的类型,也就是说在使用泛型的过程中,操作的数据类型被指定为某一参数时,改类型可以用在泛型类、泛型接口、泛型方法中。
2.特性
泛型只在编译阶段有效--->由于JVM的泛型类型擦除。
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
/*输出结果为true*/
System.out.println(l1.getClass() == l2.getClass());
在编译过程中,验证泛型的类型正确性之后,将相关信息擦除。即泛型类型在编译阶段看似多个不同的类型,实际在编译之后的阶段,都是相同的基本类型。
3.泛型的使用
- 泛型类
- 泛型接口
- 泛型方法
3.1 泛型类
通过泛型可以完成对一组类的操作对外开放相同的接口,典型有各种容器类:List、Set、Map等。
泛型类的基本写法:
class 类名 <泛型标识>{
/*该处T(泛型标识)由外部制定*/
private 泛型标识 成员变量名;
/**
*@param 自定义
*@return 返回泛型标识类型的类型
*/
public 泛型标识 成员变量名(@param T t){}
}
注
泛型标识:可以写为任意标识,常见的如T、E、K、V。
实例化泛型类时,必须制定T的具体类型(包括自定义类),不能使简单类型(如int、double)。
3.2 泛型接口
泛型接口常被用在各种类的生产器中。
泛型接口的基本写法:
public interface 接口名<泛型标识/*这里用T*/>{
public T 方法名();
}
/**
*未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
*如不声明,编译器报错:"Unknown class"
*/
class ImplGenerator<T> implements Generator<T>{
@override
public T next(){
return null;
}
}
3.3 泛型通配符
如:
public void showKetValue(Geberic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
注:
类型通配符一般是使用?代替具体的类型实参。和Integer、String一样都是一种实际的类型。
使用场景:可以解决当具体类型不确定的时候,这个通配符就是?;当操作类型时,不需要使用类型的具体功能时,只是用Object类中的功能,那么可以用?通配符来表示未知类型。
3.4 泛型方法
大多数泛型类中的成员方法也是用了泛型,甚至有的泛型类中包含着泛型方法,学习时要注意区分。
泛型类:在实例化类的时候指明泛型的具体类型;泛型方法:在调用方法的时候指明泛型的具体类型。
泛型方法可出现在任意地方和任意场景中使用。
泛型方法的基本写法:
/**
*@param t 传入泛型实参
*@return T 返回类型为T
*说明:
*1)只有声明了public和返回类型中间的<T>才是泛型方法
*2)<T>表示四该方法将使用的泛型类型为T,诸如T、E、K等,该T可出现在泛型方法任意位置
*3)Class<T>中的T表示为该类的泛型类型
*4)泛型的数量也可以为任意多个
*如:
*public <T,K> K showKeyName(Generic<T> container){...}
*/
public <T> genericMethod(Class<T> t) throws InstantiationException,
IllegalAccessExceotion{
T instance = t.newInstance();
return instance;
}
3.4.1 泛型方法与可变函数
如:
public <T> void printMsg(T...args){
for(T t:args){
Log.d("Test"," t is " + t);
}
}
3.4.2 静态方法与泛型
类中的静态方法使用泛型:静态方法无法访问类上定义的泛型,方法操作的引用数据类型不确定的时候,必须要将泛型定义在该静态方法上。
如:
public class StaticGenerator<T> {
....
/**
*如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
*即使静态方法要使用泛型类中已经声明过的泛型也不可以。
*如:public static void show(T t){..},此时编译器会提示错误信息:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){}
}
3.4.3 泛型方法总结
泛型方法可独立于类产生变化,基本原则如下:
无论何时,如果能做到,就尽量使用泛型方法,也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。另外,对于一个static的方法,无法访问泛型类型的参数。所以如果要事static方法使用泛型能力,就必须使其成为泛型方法。
4. 泛型的上下边界
- 添加上边界,即传入的类型实参必须是指定参数或其子类型
public void showKeyValue(Generic<? extends Number> obj){}
- 添加下边界,即传入的类型实参必须是制定参数或其父类
public void showKeyValue(Generic<? super Integer> obj){}
- 无边界,即通配符?,参看"3.3泛型通配符"
例子:
//在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛型声明的时候添加
//public <T> T showKeyName(Generic<T extends Number> container),编译器会报错:"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
T test = container.getKey();
return test;
}
注:泛型的上下边界添加,必须与泛型的声明在一起
原文地址:http://blog.51cto.com/13801495/2130175