Java反射

这里我们对反射的基础知识不会介绍,主要内容是相对深入一些的知识点和需要注意的点,以及给出一些使用上的示例,如果对基础知识需要了解的可以参考这边文章,Java高级特性——反射,如果侵权请联系我删除。

定义

反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取或者修改任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)

Class获取方式

反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。没有Class我们是实现不了反射的。

  • 不执行静态块和动态构造块
Class<?> clz =  String.class;
  • 执行静态块、不执行动态构造块
try {
    Class<?> clz1 = Class.forName("com.hot.lib.annotation.CoolBoy");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
  • 静态块和动态构造块均会执行
 CoolBoy coolBoy = new CoolBoy();
        coolBoy.getClass();

判断是否为某个类的实例

一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:

public native boolean isInstance(Object obj);

判断是否为某个类的类型

public boolean isAssignableFrom(Class<?> cls)

获取属性、方法、构造函数、注解的共通点

带有Declared修饰的可以获取当前类中的方法、属性,不区分private和public;
没有Declared修饰的只能获取public及从父类继承public的方法、属性。

方法和属性

class CoolBoy {
    private String str;
    public static String staticStr;
}

public class ReflectDemo {

    public static void main(String[] args) {
        CoolBoy coolBoy = new CoolBoy();
        Class<?> clz = coolBoy.getClass();

        try {
            Field str = clz.getField("str");
            //因为str是private 所以要设置访问权限
            str.setAccessible(true);
            //set的第一个参数是需要传入我们的对象,即str属性所在的对象
            str.set(coolBoy, "private");

            Field string = clz.getField("staticStr");
            //因为staticStr是静态的 所以我们不需要传入对应对象 可以传入null
            string.set(null, "static");

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }
}

数组

数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference 其中的Array类为
java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,例如:

//public static Object newInstance(Class<?> componentType, int length);
String[] strs = (String[]) Array.newInstance(String[].class.getComponentType(),10);

泛型

当我们对一个泛型类进行反射时,需要的到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通过 Type 体系来完成。 Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是:

TypeVariable 泛型类型变量。可以泛型上下限等信息。

public class TestType<K extends Comparable & Serializable, V> {
    K key;
    V value;

    public static void main(String[] args) throws Exception {
        // 获取字段的类型
        Field fk = TestType.class.getDeclaredField("key");
        Field fv = TestType.class.getDeclaredField("value");
        // getGenericType() 表示由此Field对象表示的字段的声明类型
        TypeVariable keyType = (TypeVariable) fk.getGenericType();
        TypeVariable valueType = (TypeVariable) fv.getGenericType();
        // getName 方法
        System.out.println(keyType.getName());   // K
        System.out.println(valueType.getName()); // V
        // getGenericDeclaration  获取声明该类型变量实体
        System.out.println(keyType.getGenericDeclaration()); // class com.test.TestType
        System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
        // getBounds 方法
        System.out.println("K 的上界:"); // 有两个
        for (Type type : keyType.getBounds()) { // interface java.lang.Comparable
            System.out.println(type); // interface java.io.Serializable
        }
        System.out.println("V 的上界:"); // 没明确声明上界的, 默认上界是 Object
        for (Type type : valueType.getBounds()) { // class java.lang.Object
             System.out.println(type);
        }
    }

}

ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)

public class TestType {
    Map<String, String> map;
    public static void main(String[] args) throws Exception {
        Field f = TestType.class.getDeclaredField("map");
        System.out.println(f.getGenericType());                               // java.util.Map<java.lang.String, java.lang.String>
        System.out.println(f.getGenericType() instanceof ParameterizedType);  // true
        ParameterizedType pType = (ParameterizedType) f.getGenericType();
        System.out.println(pType.getRawType());                               // interface java.util.Map
        for (Type type : pType.getActualTypeArguments()) {
            System.out.println(type);                                         // 打印两遍: class java.lang.String
        }
        System.out.println(pType.getOwnerType());                             // null
    }
}

GenericArrayType 当需要描述的类型是泛型类的数组时,组成数组的元素中有范型则实现了该接口;比如List[],Map[],此接口会作为Type的实现。

public class TestType<T> {
    List<String>[] lists;

    public static void main(String[] args) throws Exception {
        Field f = TestType.class.getDeclaredField("lists");
        GenericArrayType genericType = (GenericArrayType) f.getGenericType();
        System.out.println(genericType.getGenericComponentType());  //java.util.List<java.lang.String>
    }
}

WildcardType 通配符泛型,获得上下限信息。

public class TestType {
    private List<? extends Number> a;  // // a没有下界, 取下界会抛出ArrayIndexOutOfBoundsException
    private List<? super String> b;
    public static void main(String[] args) throws Exception {
        Field fieldA = TestType.class.getDeclaredField("a");
        Field fieldB = TestType.class.getDeclaredField("b");
        // 先拿到范型类型
        Assert.that(fieldA.getGenericType() instanceof ParameterizedType, "");
        Assert.that(fieldB.getGenericType() instanceof ParameterizedType, "");
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
        // 再从范型里拿到通配符类型
        Assert.that(pTypeA.getActualTypeArguments()[0] instanceof WildcardType, "");
        Assert.that(pTypeB.getActualTypeArguments()[0] instanceof WildcardType, "");
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
        // 方法测试
        System.out.println(wTypeA.getUpperBounds()[0]);   // class java.lang.Number
        System.out.println(wTypeB.getLowerBounds()[0]);   // class java.lang.String
        // 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
        System.out.println(wTypeA);
    }
}

泛型实际使用中的问题

我们实际开发中都会存在对后台数据统一解析的处理,我们一般都会统一一个Response,例如:

 static class Response<T> {
        T data;
        int code;
        String message;

        @Override
        public String toString() {
            return "Response{" +
                    "data=" + data +
                    ", code=" + code +
                    ", message='" + message + '\'' +
                    '}';
        }

        public Response(T data, int code, String message) {

            this.data = data;
            this.code = code;
            this.message = message;
        }
    }

    static class Data {
        String result;

        public Data(String result) {
            this.result = result;
        }

        @Override
        public String toString() {
            return "Data{" +
                    "result=" + result +
                    '}';
        }
    }

下面我进行解析处理

 public static void main(String[] args) {
        Response<Data> dataResponse = new Response(new Data("数据"), 1, "成功");

        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

        //反序列化......

        /**
         *  有花括号: 代表是匿名内部类,创建一个匿名内部类的实例对象
         *  没花括号:创建实例对象
         */
        Type type = new TypeRefrence<Response<Data>>(){}.getType();

        System.out.println(type);
        Response<Data> response = gson.fromJson(json, type);
        System.out.println(response.data.getClass());
    }

这里我们注意这里
Type type = new TypeRefrence<Response<Data>>(){}.getType();
TypeRefrence是Gson提供的类,用来获取泛型类型的,而这里我们可以看到上面这实际上是一个匿名内部类,如果我们将其改为下面这种形式
Type type = new TypeRefrence<Response<Data>>().getType();
这样代码还能编译通过吗?答案是否定的,因为没有了泛型的类型信息。
因为只有定义为抽象类或者接口,这样在使用时,需要创建对应的实现类,此时确定泛型类型,编译才能够将泛型signature信息记录到Class元数据中。 这就是因为泛型擦除后,我们还可以通过反射取到泛型类型的原因。所以这里我们要注意。
其实我们也可以自己实现一个TypeRefrence,代码如下

class TypeRefrence<T> {
        private Type type;
        
        //注意这里我们的访问权限是protected 这样不在一个包下的时候 就必须要创建内部类 不能直接实例化
        protected TypeRefrence() {
            Type genericSuperclass = getClass().getGenericSuperclass();
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            //因为我们这里只有一个泛型 所以我们直接取第一个
            type = actualTypeArguments[0];
        }

        public Type getType() {
            return type;
        }
    }

自动获取View id 示例

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewAnnotation {
    @IdRes int value();
}

public static void getId(Activity activity) {
        Field[] declaredFields = activity.getClass().getDeclaredFields();

        for (Field filed : declaredFields) {
            //该字段是否被ViewAnnotation注解声明
            if (filed.isAnnotationPresent(ViewAnnotation.class)) {
                ViewAnnotation viewAnnotation = filed.getAnnotation(ViewAnnotation.class);
                //获取注解中的id
                int id = viewAnnotation.value();
                View view = activity.findViewById(id);
                //设置访问权限
                filed.setAccessible(true);
                try {
                    //反射设置属性的值
                    filed.set(activity, view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

自动获取Intent传递的参数

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
     String value() default "";
}

public static void injectAutowired(Activity activity) {
        Class<? extends Activity> cls = activity.getClass();
        //获得数据
        Intent intent = activity.getIntent();
        Bundle extras = intent.getExtras();
        if (extras == null) {
            return;
        }

        //获得此类所有的成员
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(Autowired.class)) {
                Autowired autowired = field.getAnnotation(Autowired.class);
                //获得key
                String key = TextUtils.isEmpty(autowired.value()) ? field.getName() : autowired.value();

                if (extras.containsKey(key)) {
                    Object obj = extras.get(key);
                    // todo Parcelable数组类型不能直接设置,其他的都可以.
                    //获得数组单个元素类型
                    Class<?> componentType = field.getType().getComponentType();
                    //当前属性是数组并且是 Parcelable(子类)数组
                    if (field.getType().isArray() &&
                            Parcelable.class.isAssignableFrom(componentType)) {
                        Object[] objs = (Object[]) obj;
                        //创建对应类型的数组并由objs拷贝

                        Object[] objects = Arrays.copyOf(objs, objs.length, (Class<? extends Object[]>) field.getType());
                        obj = objects;
                    }
                    field.setAccessible(true);
                    try {
                        field.set(activity, obj);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容