泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数,在用到的时候再指定具体的类型。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
在Java中,采用泛型擦除的机制来引入泛型,泛型能编译器使用javac时确保数据的安全性和免去强制类型转换问题,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。并且一旦编译完成,所有和泛型有关的类型会被全部擦除。
Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType等几种类型,能让我们通过反射操作这些类型。
ParameterizedType:表示一种参数化类型,比如Collection<String>
GenericArrayType:表示种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表种通配符类型表达式
package com.nobody; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Map; public class TestReflectGenerics { public Map<String, Person> test(Map<String, Integer> map, Person person) { return null; } public static void main(String[] args) throws NoSuchMethodException { // 获取test方法对象 Method test = TestReflectGenerics.class.getDeclaredMethod("test", Map.class, Person.class); // 获取方法test的参数类型 Type[] genericParameterTypes = test.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("方法参数类型:" + genericParameterType); // 如果参数类型等于参数化类型 if (genericParameterType instanceof ParameterizedType) { // 获得真实参数类型 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(" " + actualTypeArgument); } } } // 获取方法test的返回值类型 Type genericReturnType = test.getGenericReturnType(); System.out.println("返回值类型:" + genericReturnType); // 如果参数类型等于参数化类型 if (genericReturnType instanceof ParameterizedType) { // 获得真实参数类型 Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(" " + actualTypeArgument); } } } } class Person {} // 输出结果 方法参数类型:java.util.Map<java.lang.String, java.lang.Integer> class java.lang.String class java.lang.Integer 方法参数类型:class com.nobody.Person 返回值类型:java.util.Map<java.lang.String, com.nobody.Person> class java.lang.String class com.nobody.Person 反射操作注解在Java运行时,通过反射获取代码中的注解是比较常用的手段了,获取到了注解之后,就能知道注解的所有信息了,然后根据信息进行相应的操作。下面通过一个例子,获取类和属性的注解,解析映射为数据库中的表信息。
package com.nobody; import java.lang.annotation.*; public class AnalysisAnnotation { public static void main(String[] args) throws Exception { Class<?> aClass = Class.forName("com.nobody.Book"); // 获取类的指定注解,并且获取注解的值 Table annotation = aClass.getAnnotation(Table.class); String value = annotation.value(); System.out.println("Book类映射的数据库表名:" + value); java.lang.reflect.Field bookName = aClass.getDeclaredField("bookName"); TableField annotation1 = bookName.getAnnotation(TableField.class); System.out.println("bookName属性映射的数据库字段属性 - 列名:" + annotation1.colName() + ",类型:" + annotation1.type() + ",长度:" + annotation1.length()); java.lang.reflect.Field price = aClass.getDeclaredField("price"); TableField annotation2 = price.getAnnotation(TableField.class); System.out.println("price属性映射的数据库字段属性 - 列名:" + annotation2.colName() + ",类型:" + annotation2.type() + ",长度:" + annotation2.length()); } } // 作用于类的注解,用于解析表数据 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Table { // 表名 String value(); } // 作用于字段,用于解析表列 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface TableField { // 列名 String colName(); // 列类型 String type(); // 长度 int length(); } @Table("t_book") class Book { @TableField(colName = "name", type = "varchar", length = 15) String bookName; @TableField(colName = "price", type = "int", length = 10) int price; } // 输出结果 Book类映射的数据库表名:t_book bookName属性映射的数据库字段属性 - 列名:name,类型:varchar,长度:15 price属性映射的数据库字段属性 - 列名:price,类型:int,长度:10 性能分析