泛型的好处
1.不会在运行期报错,避免代码在上线后运行你报错,提前在编译期告诉用户哪里错误
2.不用强制转换
泛型类
子类是泛型类,子类和父类的泛型类型要一致,如父类是E,子类是T,子类继承父类时,父类也要用T,而不是写E。子类可以扩展泛型,如A<T,S>,该泛型类定义后,相当于字段的用法,如构造方法初始化,使用setter和getter方法。
子类不是泛型类,则继承的父类要明确具体类型。
泛型类型只能是包装类,因为都是继承Object
两者不指定父类是什么类型,则为Object类型。
泛型接口和泛型类使用一致。
List和List对象的类型是一样的,一样是List类型。
泛型方法
方法用到了泛型列表才是泛型方法,如,而T的方法还是普通方法,并不是泛型方法。
泛型方法的泛型类型跟泛型类或接口的泛型类型无关。支持静态
成员方法的泛型类型则要遵从泛型类或接口的泛型类型,即普通方法。不支持静态
泛型方法支持可变参数,如 void a(E… e)
能用泛型方法就不要用泛型类,因为泛型方法更加灵活。
类型通配符
类型通配符:表示具体的实参,如?,他相当于Object,他是类型实参,不是类型形参
类型通配符上限
<? extends A>:只能是A的子类或者是A。上限最大是A,A则为上限。遍历是A,因为都是A的子类。
方法参数是ArrayList< ? extends A >, 则方法里不能添加具体类型,如list.add(new A()) ,因为类型不确定。
类型通配符下限
<? super A>:只能是A或者A的父类,A则为下限。遍历只能是Object
方法参数是ArrayList<? super A>, 则方法里可以添加任何具体类型,因为他遍历的是Object,即他的父类不能确定,只能用Object接收。
类型擦除
为了兼容jdk1.5,只在编译时有泛型存在,编译完成后就擦除泛型。如
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}
结果为true,因为他们的实际类型是ArrayList,而不是ArrayList和ArrayList,因为类型被擦除了。
无限制类型擦除
public class Erasure<T> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
public class Test {
public static void main(String[] args) {
Erasure<Integer> era = new Erasure<>();
// 反射,获取Erasure类的字节码文件的Class类对象
Class<? extends Erasure> class1 = era.getClass();
// 获取所有的成员变量
Field[] declaredFields = class1.getDeclaredFields();
// 遍历数组,打印成员变量的名称和类型
for (Field f : declaredFields) {
System.out.println(f.getName() + ":" + f.getType().getSimpleName());
}
}
}
执行结果如图
有限制类型擦除
public class Erasure<T extends Number> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
public class Test {
public static void main(String[] args) {
Erasure<Integer> era = new Erasure<>();
// 反射,获取Erasure类的字节码文件的Class类对象
Class<? extends Erasure> class1 = era.getClass();
// 获取所有的成员变量
Field[] declaredFields = class1.getDeclaredFields();
// 遍历数组,打印成员变量的名称和类型
for (Field f : declaredFields) {
System.out.println(f.getName() + ":" + f.getType().getSimpleName());
}
}
}
结果如图
擦除方法中类型定义的参数
public class Erasure<T extends Number> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
// 泛型方法,上限为List
public <T extends List> T show(T t) {
return t;
}
}
Erasure<Integer> era = new Erasure<>();
// 反射,获取Erasure类的字节码文件的Class类对象
Class<? extends Erasure> class1 = era.getClass();
//擦除方法中类型定义的参数
// 获取Erasure下所有的方法
Method[] declaredMethods = class1.getDeclaredMethods();
for (Method m : declaredMethods) {
// 打印方法名和方法的返回值类型
System.out.println(m.getName() + ":" + m.getReturnType().getSimpleName());
}
执行结果如图
桥接方法
/**
*
* 泛型接口
* @param <T>
*/
public interface Info<T> {
T info(T t);
}
public class InfoImpl implements Info<Integer> {
@Override
public Integer info(Integer value) {
return value;
}
}
/**
* 桥接方法
*/
Class<InfoImpl> infoClass = InfoImpl.class;
//获取所有方法
Method[] infoImplMethods = infoClass.getDeclaredMethods();
for (Method m : infoImplMethods) {
//打印方法名和方法的返回值类型
System.out.println(m.getName() + ":" + m.getReturnType().getSimpleName());
}
结果如下图
泛型数组
public class Fruit<T> {
private T[] array;
public Fruit(Class<T> clz,int length){
//创建泛型数组
array = (T[]) Array.newInstance(clz,length);
}
//数组赋值
public void put(int index,T t){
array[index] = t;
}
}