泛型
使用泛型创建实例时,编译器来负责转型,确保类型正确
- 代码的安全性检查提前到编译期
- 对传入值进行编译期检查,对传递出去的值自动转型,简化代码
- 泛型参数不能为基本类型
- 类型推断只对赋值操作有效,其他操作编译器不执行类型推断
- 相比于使用Object,泛型可以暂不指定类型,由编译器负责转型
泛型类
在类名后使用尖括号来指定类持有什么类型的对象,创建对象时要指定参数类型,静态方法要使用泛型能力必须声明为泛型方法
public class A<K,V>{
K key;
V val;
public K getKey(){
return key;
}
public void setKey(K key){
this.key=key;
}
}
泛型方法
定义泛型方法是在方法返回类型前定义泛型参数列表,泛型方法独立于泛型类,使用泛型方法通常不必指定参数类型(显示声明:类名/this.<类型> print() )
public <T> void print(T val){
System.out.println(val);
}
泛型擦除
编译后具体类型被擦除, 在泛型代码内部无法获得任何有关泛型参数类型的信息
public class Base<T> {
public T value;
public Base(){}
public Base(T v){
value=v;
}
public T getValue(){
return value;
}
public void setValue(T value){
this.value=value;
}
}
未设置边界
参数类型被擦除到Object
public static void main(String[] args) {
Base<String> base = new Base<>();
base.setValue("king");
}
设置边界
语法<T extends ClassA & InterfaceA & interfaceB>,设置边界也是对泛型擦除的一种补偿机制,使参数类型能够调用部分方法
class Test{}
class A extends Test{}
//设置泛型类型T的上界Test
public class Base<T extends Test> {
······
}
public static void main(String[] args) {
Base<A> base = new Base<>();
base.setValue(new A());
}
可以看出参数类型被擦除到Test
协变与逆变
两个类型之间建立某种类型的向上转型关系
- 数组是协变的(可将子类型引用赋值给基类型)
- 泛型是不变的(类型不同直接编译时报错)
因为泛型是不变的,当泛型需要使用协变和逆变时则需引入通配符<?>
协变实现:<? extends A>,不可写入,可获得A及其父类
逆变实现:<?super A>,可写入A及其子类,不可获得
泛型的获取
泛型的擦除是针对Code中字节码的,编译后在signature属性中还是保留了泛型参数类型,我们可以通过反射获取对应的参数类型。
LocalVariableTypeTable:本地变量类型表存在于Code属性中,提供变量类型的签名信息
Signature:存在类结构、字段表、方法表中,记录泛型签名信息
Type接口
标识接口,所有类型的通用超级接口。 这些包括原始类型,参数化类型,数组类型,类型变量和原始类型。
public interface Type {
}
ParameterizedType 接口
参数化类型接口
public interface ParameterizedType extends Type {
//返回此类型的实际类型参数的数组,如A<String>返回String
Type[] getActualTypeArguments();
//返回当前声明的类型,如A<String>返回A
Type getRawType();
//返回所属类型
Type getOwnerType();
}
常见获取方法
public static void main(String[] args) {
Base<String,Integer> map = new Base<String,Integer>(){};
//使用匿名内部类获得对象父类持有的泛型类型
Type genericSuperclass = map.getClass().getGenericSuperclass();
if(genericSuperclass instanceof ParameterizedType){
Type[] types = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
System.out.println(Arrays.toString(types));
}
try {
//获得成员变量的参数类型 List<String> list;
Base<String,Integer> map2 = new Base<>();
Field list = map2.getClass().getDeclaredField("list");
Type genericType = list.getGenericType();
if(genericType instanceof ParameterizedType){
Type[] types = ((ParameterizedType) genericType).getActualTypeArguments();
System.out.println(Arrays.toString(types));
}
//获得方法的参数类型 public T test(ArrayList<String> list){}
Method method = map2.getClass().getDeclaredMethod("test", ArrayList.class);
//传入参数类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
for(Type type:genericParameterTypes){
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
System.out.println(Arrays.toString(types));
}
//返回参数类型
ParameterizedType genericReturnType = (ParameterizedType)method.getGenericReturnType();
System.out.println(Arrays.toString(genericReturnType.getActualTypeArguments()));
} catch (Exception e) {
e.printStackTrace();
}
}
输出结果如下
[class java.lang.String, class java.lang.Integer]
[class java.lang.String]
[class java.lang.String]
[class java.lang.String]
自限定类型 class A<T extends A<T>>,保证类型参数与正在被定义的类相同