1.泛型定义
泛型的本质是为了类型参数化,一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么类型参数化怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参). 注意:泛型只在编译中有效
2.泛型使用
- 泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
2.1 泛型类
使用语法: Generic<String> generic = new Generic<String>();
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
2.1.1 泛型类派生子类
- 子类若也是泛型类 ,则子类泛型要与父类泛型类型保持一致
public class Children<T> extends Generic<T>
- 子类不是泛型类,则父类要指定类型
public class Children extends Generic<String>
注意:
- 泛型的类型参数只能是类类型,不支持基础类型。
- 使用的时候若不指定类型 则默认是Object类型。
- 泛型在逻辑上看成多个不同的类型,实质上是相同类型
2.2 泛型接口
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
注意:
- 当实现类是泛型类,必须与实现的接口泛型类型的定义一样,不然则编译报错
正确示例:
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
错误示例:
class FruitGenerator implements Generator<T>{
@Override
public T next() {
return null;
}
}
- 在实现类不是泛型类,则实现的接口要明确数据类型
public class FruitGenerator implements Generator<String> {
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
2.3 泛型方法
泛型方法,是在调用方法的时候指明泛型的具体类型 。
/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
2.3.1 泛型方法与可变参数
使用 T… args
public <T> void printMsg( T... args){
for(T t : args){
Log.d("泛型测试","t is " + t);
}
}
2.3.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){
}
}
2.4 泛型通配符
同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的,即使是父子类都不可以
类型通配符一般是使用?代替具体的类型实参,此处’?’是类型实参,而不是类型形参,未指定则是Object
public void showKeyValue1(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
2.5 泛型上下边界
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。
2.5.1 上边界(即传入的类型实参必须是指定类型本身或它的子类)
public void showKeyValue1(Generic<? extends Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
2.5.2 下边界(即传入的类型实参必须是指定类型本身或其祖类)
public void showKeyValue2(Generic<? super Number> obj){
System.out.println("泛型测试 key value is " + obj.getKey());
}
注意://在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的上添加上下边界,即在泛型声明的时候添加 如下图:
//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;
}
2.6 泛型擦除
1. 无限制类型擦除 ,将泛型变为Object
2. 有限制类型擦除 ,将泛型变为上线边界的类型
3. 桥接方法–保持接口与类的实现关系