美文网首页android技术
29.Android架构-注解与反射

29.Android架构-注解与反射

作者: 任振铭 | 来源:发表于2020-09-04 08:22 被阅读0次

注解的作用或者意义是什么?

注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他如反射、插桩等技术才有意义。

Java 注解(Annotation)又称 Java 标注,是 JDK1.5 引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

注解保留级别@Retention

RetentionPolicy.SOURCE
标记的注解仅保留在源码级别中,并被编译器忽略。

RetentionPolicy.CLASS
标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。

RetentionPolicy.RUNTIME
标记的注解由 JVM 保留,因此运行时环境可以使用它。

SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS

不同保留级别注解的应用场景

1.源码级别 ( APT技术(Anotation Processor Tools) )

在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。源码级别的注解,可提供给IDE语法检查(@Target({ANNOTATION_TYPE}))、APT等场景使用如ButterKnife,在类中使用 SOURCE 级别的注解,其编译之后的class中会被丢弃。

注解处理器是 javac 自带的一个工
具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac调起,并将注解信息传递给注解处理器进行处理。注解处理器是对注解应用最为广泛的场景。在Glide、EventBus3、Butterknifer、Tinker、ARouter等等常用框架中都有注解处理器的身影。但是你可能会发现,这些框架中对注解的定义并不是 SOURCE 级别,更多的是 CLASS 级别,别忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。

2.字节码级别 (字节码增强技术)

在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。如插桩
定义为 CLASS 的注解,会保留在class文件中,但是会被虚拟机忽略(即无法在运行期反射获取注解)。此时完全符合此种注解的应用场景为字节码操作。如:AspectJ、热修复Roubust中应用此场景。所谓字节码操作即为,直接修改字节码Class文件以达到修改代码执行逻辑的目的。

3.运行时级别 (反射技术)

注解保留至运行期,意味着我们能够在运行期间结合反射技术获取注解中的所有信息。
在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。如一些比较古老的框架在运行时通过反射进行view的findViewById操作,或者Gson中也使用到了这个能力

反射

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

反射如何获取泛型的真实类型?

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

TypeVariable

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

public class TestFanxing<K extends Comparable & Serializable, V> {

    K key;
    V value;

    public static void main(String[] args) throws Exception {
        // 获取字段的类型
        Field fk = TestFanxing.class.getDeclaredField("key");
        Field fv = TestFanxing.class.getDeclaredField("value");
        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);
        }
    }
}

打印结果:
K
V
class TestFanxing
class TestFanxing
K 的上界:
interface java.lang.Comparable
interface java.io.Serializable
V 的上界:
class java.lang.Object
ParameterizedType

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

public class TestFanxing<K extends Comparable & Serializable, V> {
    Map<String, String> map;
    public static void main(String[] args) throws Exception {
        Field f = TestFanxing.class.getDeclaredField("map");
        System.out.println(f.getGenericType());
        // java.util.Map<java.lang.String, java.lang.String>
        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
        }
    }
}

java.util.Map<java.lang.String, java.lang.String>
interface java.util.Map
class java.lang.String
class java.lang.String
GenericArrayType

当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。

public class TestFanxing<K extends Comparable & Serializable, V> {
    List<String>[] lists;
    public static void main(String[] args) throws Exception {
        Field f = TestFanxing.class.getDeclaredField("lists");
        GenericArrayType genericType = (GenericArrayType) f.getGenericType();
        System.out.println(genericType.getGenericComponentType());
    }
}

java.util.List<java.lang.String>
WildcardType

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

public class TestFanxing<K extends Comparable & Serializable, V> {

    private List<? extends Number> a;
    private List<? super String> b;

    public static void main(String[] args) throws Exception {
        Field fieldA = TestFanxing.class.getDeclaredField("a");
        Field fieldB = TestFanxing.class.getDeclaredField("b");
        //先拿到范型类型
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
        //再从范型里拿到通配符类型
        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);
    }
}

class java.lang.Number
class java.lang.String
? extends java.lang.Number

Gson反序列化

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);
        //为什么TypeToken要定义为抽象类? 
        Response<Data> resp = gson.fromJson(json, new TypeToken<Response<Data>>() {
        }.getType());
        System.out.println(resp.data.result);
    }

在进行GSON反序列化时,存在泛型时,可以借助 TypeToken 获取Type以完成泛型的反序列化。但是为什么
TypeToken 要被定义为抽象类呢?
因为只有定义为抽象类或者接口,这样在使用时,需要创建对应的实现类,此时确定泛型类型,编译才能够将泛型
signature信息记录到Class元数据中。

相关文章

  • 29.Android架构-注解与反射

    注解的作用或者意义是什么? 注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他如反射、插桩等技术才有意义...

  • 反射与注解

    反射:框架设计的灵魂 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码反射:将类的各个组成部分封装为其他...

  • 注解与反射

    注解 Annotation, JDK5.0 引入的一种注释机制 注解是元数据的一种形式,提供有关于程序但不属于程序...

  • 注解与反射

    注解(Annotation) 什么是注解 注解又叫 Java 标注,是 JDK5.0 引入的一种注释机制。注解是元...

  • 注解与反射

    注解与反射 自定义注解 @Target 描述的注解可以用在什么地方@Retention 表示被它所注解的注解在...

  • 注解与反射

    注解 声明一个注解类型 元注解 在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之...

  • 第12章 元编程与注解、反射

    第12章 元编程与注解、反射 反射(Reflection)是在运行时获取类的函数(方法)、属性、父类、接口、注解...

  • java注解与反射,泛型与反射

    一、反射与注解 内置注解 java内置了3种注解,用来为编译器提供检查。 自定义注解 元注解 元注解是用来修饰注解...

  • 一文搞懂反射泛型和反射注解以及通过注解方式写一个BaseDao

    反射泛型和反射注解概括起来就三步: 自定义注解 通过反射获取注解值 使用自定义注解 最终案例 通过自定义注解,将数...

  • 通过自定义注解A类给B类赋值

    自定义注解与反射说明 元注解:1)@Retention:RetentionPolicy.SOURCE :只在jav...

网友评论

    本文标题:29.Android架构-注解与反射

    本文链接:https://www.haomeiwen.com/subject/imfnsktx.html