本文转载自微信公众号「潜行前行」,基础作者cscw 。篇深转载本文请联系潜行前行公众号。入解
1 JAVA的析J型Type类型体系
先了解下java的Type类型体系(类的类=>类型),Type是基础所有类型(原生类型-Class、参数化类型-Parameterizedtype、篇深数组类型-GenericArrayType、入解类型变量-TypeVariable、析J型基本类型-Class)的基础共同接口;前两篇反射和注解讲到的Class就是Type的一实现类
2 泛型的概念
Java 泛型(generics)是JDK1.5中引入的一个新特性,其本质是参数化类型,解决不确定具体对象类型的问题;其所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法泛型: 把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型
3 泛型类和泛型方法的示例
泛型类的定义 public class MainTest<T> { private T param; } public static void main(String[] args){ MainTest<String> data = new MainTest<String>(){ }; ParameterizedType genType1 = (ParameterizedType)data.getClass().getGenericSuperclass(); } 泛型方法的香港云服务器定义 public class MainTest{ public static void main(String[] args){ printData("siting"); } static <T> T printData(T t){ System.out.println(t); return t; } }接口和抽象类都可以使用泛型
4 类型擦除
创建泛型的实例时,jvm是会把具体类型擦除的;编译生成的字节码中不包含泛型中的类型参数,即ArrayList和ArrayList都擦除成了ArrayList,也就是被擦除成"原生类型",这就是泛型擦除 public class MainTest { public static void main(String[] args){ List<String> strArr = new ArrayList<>(); List<Integer> intArr = new ArrayList<>(); Type strClazz = strArr.getClass(); Type intClazz = intArr.getClass(); } }可以看出T(String)都被转换为Object类型,最初的初始化的String不见了
5 泛型的继承
子类可以指定父类的泛型参数,可以是已知类(Integer、String等),也可以用子类自己的泛型参数指定 泛型被继承时,且指定父类泛型参数,则额外生成的ParameterizedType类型作为子类的源码下载父类;如果没有指定父类泛型参数,则直接继承原生类型 public class MainTest<T> { T param; static public class SubTest1 extends MainTest<String>{ } static public class SubTest2<R> extends MainTest<R>{ } //SubTest3继承的时原生类型 static public class SubTest3 extends MainTest{ } }6 泛型变量TypeVariable
(先临时定义一个名称,Test里的E为泛型参数);泛型变量TypeVariable:泛型的泛型参数就是TypeVariable;当父类使用子类的泛型参数指定自身的泛型参数时;或者泛型属性定义在泛型类A中,并使用泛型类A的泛型参数T时,其泛型参数都会被编译器定为泛型变量TypeVariable,而不是被擦除 public class MainTest<T> { List<T> param; public static void main(String[] args) throws Exception{ Class clazz = MainTest.class; TypeVariable[] typeVariable = clazz.getTypeParameters(); // 1 Field field = clazz.getDeclaredField("param"); ParameterizedType arrayType = (ParameterizedType)field.getGenericType(); // interface List<E> 的泛型类型E被T,具体化,因此其被识别为 TypeVariable TypeVariable variable1 = (TypeVariable)arrayType.getActualTypeArguments()[0]; // 2 ParameterizedType type = (ParameterizedType)SubTest.class.getGenericSuperclass(); TypeVariable variable2 = (TypeVariable)type.getActualTypeArguments()[0]; } static class SubTest<R> extends MainTest<R>{ } }7 参数化类型ParameterizedType
public interface ParameterizedType extends Type { //获取实际参数,List<String>里的String; 如果是List<T>则是TypeVariable类型 Type[] getActualTypeArguments(); // 获取原始类型List<String> -> List<E> Type getRawType(); Type getOwnerType(); } 需要注意的点,我们不能直接获取指定具体参数的泛型的类型,如Class clazz = List.class编译时不通过的;还有就是直接通过泛型类new创建的对象,其Class并非ParameterizedType类型,而是泛型本身的class,示例如下 public class MainTest<T> { public static void main(String[] args){ MainTest<String> str = new MainTest<String>(); Class variable = str.getClass(); Type genType1 = variable.getGenericSuperclass(); } }8 通配符(WildcardType)
无边界通配符:无界通配符 ? 可以适配任何引用类型:
当方法参数需要传入一个泛型时,而且无法确定其类型时。直接使用无具体泛型变量的泛型,容易造成安全隐患;若在方法代码里进行类型转换,极容易出现ClassCastException错误 那泛型变量用Object代替不就行了?但是泛型类+具体参数转变的ParameterizedType(参数化类型)是不存在继承关系;即Object是String的父类,但是List 和List的类型是不同的两个ParameterizedType,不存在继承关系。于是有了类型通配符 ? public static void print(List list){ } ----->>> public static void print(List<?> list){ }上界限定通配符 < ? extends E>
想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做?可以使用List,表明List里的元素都是Number的子类 public static void print(List<? extends Number> list) { Number n = new Double("1.0"); list.add(n); Number tmp = list.get(0); }下界限定通配符 < ? super E>
class Parent{ } class Child extends Parent{ } public class MainTest<T> { T param; public static void main(String[] args){ MainTest<? super Child> parent_m = new MainTest<>(); parent_m.setParam(new Child()); Object parent = parent_m.getParam(); } public T getParam() { return param; } public void setParam(T param) { this.param = param; } }9 泛型数组(GenericArrayType)
public interface GenericArrayType extends Type { //获得这个数组元素类型,即获得:A<T>(A<T>[])或 T(T[]) Type getGenericComponentType(); } GenericArrayType,泛型数组,描述的是ParameterizedType类型以及TypeVariable类型数组,即形如:Test[][]、T[]等,是GenericArrayType的子接口 public class MainTest<T> { T[] param; public static void main(String[] args) throws Exception{ Class clazz = MainTest.class; Field field = clazz.getDeclaredField("param"); GenericArrayType arrayType = (GenericArrayType)field.getGenericType(); TypeVariable variable = (TypeVariable) arrayType.getGenericComponentType(); } }