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 和返回类型中间的 才是泛型方法
- *2) 表示四该方法将使用的泛型类型为 T, 诸如 T,E,K 等, 该 T 可出现在泛型方法任意位置
- *3)Class 中的 T 表示为该类的泛型类型
- *4) 泛型的数量也可以为任意多个
- * 如:
- *public K showKeyName(Generic 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 泛型通配符"
例子:
- // 在泛型方法中添加上下边界限制的时候, 必须在权限声明与返回值之间的 上添加上下边界, 即在泛型声明的时候添加
- //public T showKeyName(Generic 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://www.bubuko.com/infodetail-2648208.html