java中的反射(二)

    今天我来继续介绍java中的反射,上一次我介绍了Class对象以及如何获取Class对象,反射的入口就是Class对象,通过Class对象我可以获取类中的属性和方法也可以创建类的实例对象,在平时的开发中反射技术的使用时非常频繁的,所以掌握反射技术是非常有必要的。

    反射和泛型

    在我介绍泛型的时候曾经说过泛型参数会在运行的时候被擦除,这里需要注意一下,在字节码对象Class中依然有关于泛型的信息,可以通过反射得到。在Class对象中有一个getTypeParameters()方法会返回一个TypeVariable 的对象数组其中TypeVariable表示的是一个类型变量也就是我们在定义泛型时是所使用的类型参数。在Field对象中有一个方法getGenericType()返回一个Type对象。在Method对象中有getGenericReturnType()返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象;Type[] getGenericParameterTypes()按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的;getGenericExceptionTypes()返回 Type 对象数组,这些对象描述了声明由此 Method 对象抛出的异常。在Constructor对象中有getGenericParameterTypes()按照声明顺序返回一组 Type 对象,这些对象表示此 Constructor 对象所表示的方法的形参类型。下面我们来看一个具体的例子:

public class User <K extends Number,V extends Comparable<V>>{
     K k;
     V v;
     Set<String> list;
     public <T> T test1(T t){
         return null;
     }
     public V test2(List<? extends Number> param){
         return null;
     }
    public static void main(String[] args) throws Exception {
         Class<?> cls = Class.forName("com.reflect.generic.User");
         
         TypeVariable<?>[] types =  cls.getTypeParameters();
         //类上的泛型参数
         for (TypeVariable<?> typeVariable : types) {
            System.out.println(typeVariable.getName());
         }
         //字段上的泛型参数
         Field[] fields = cls.getDeclaredFields();
         for (Field field : fields) {
            System.out.println(field.getName()+":"+field.getGenericType().getTypeName());
        }
        
        //方法上的泛型参数
         Method method1 = cls.getMethod("test1", new Class[]{Object.class});
         Type[] methodType1 =method1.getGenericParameterTypes();
         for (Type type : methodType1) {
            System.out.println(method1.getName()+":"+type.getTypeName());
         }
         
         Method method2 = cls.getMethod("test2", new Class[]{List.class});
         Type[] methodType2 =method2.getGenericParameterTypes();
         for (Type type : methodType2) {
                System.out.println(method2.getName()+":"+type.getTypeName());
         }
    }
}

输出的结果为:

K
V
k:K
v:V
list:java.util.Set<java.lang.String>
test1:T
test2:java.util.List<? extends java.lang.Number>

    反射虽然功能强大但是请不要太依赖反射甚至滥用反射技术。因为当我们使用反射时更容易出现运行期错误,在我们使用显式的类和接口的时候java编译器会帮我们做运行期检查,许多的异常要求我们在编译期处理可是使用了反射,类型是在运行时才能知道这时的编译器已经鞭长莫及了!同时我们需要知道的是反射的性能是要低一点的,所以如果能使用类和接口解决问题就不要使用反射。