如何实现自定义注解

注解(Annotation)在Java中,是以@字符开始的修饰符,如我们常见的@Override就是注解。而它的源码如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

可以看到源码很简单,那么这些@Target@Retention是什么意思呢?这些在自定义注解上的注解,叫做元注解

元注解

Java 中提供了以下元注解类型:

  • @Retention
  • @Target
  • @Documented
  • @Inherited(JDK8 引入)
  • @Repeatable(JDK8 引入)

常用的就@Target@Retention

@Retention

指明了注解的生命周期。如:

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD) 
public @interface TestSource {
}

RetentionPolicy 是一个枚举类型,它定义了被 @Retention 修饰的注解所支持的保留级别:

  • RetentionPolicy.SOURCE - 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
  • RetentionPolicy.CLASS - 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
  • RetentionPolicy.RUNTIME - 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
生命周期长度:RUNTIME > CLASS > SOURCE
在这里插入图片描述在这里插入图片描述
上述图片也证明了SOURCE只保留在源文件中。

@Target

指定注解可以修饰的元素类型(如方法、类、字段)。如:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestRunTime {
}

ElementType也是一个枚举类型

  • ElementType.ANNOTATION_TYPE - 标记的注解可以应用于注解类型。
  • ElementType.CONSTRUCTOR - 标记的注解可以应用于构造函数。
  • ElementType.FIELD - 标记的注解可以应用于字段或属性。
  • ElementType.LOCAL_VARIABLE - 标记的注解可以应用于局部变量。
  • ElementType.METHOD - 标记的注解可以应用于方法。
  • ElementType.PACKAGE - 标记的注解可以应用于包声明。
  • ElementType.PARAMETER - 标记的注解可以应用于方法的参数。
  • ElementType.TYPE - 标记的注解可以应用于类的任何元素。
@Documented

指定注解会被JavaDoc工具提取成文档。 默认情况下,JavaDoc 是不包括注解的。

@Inherited

表示该注解会被子类继承,注意,仅针对类,成员属性、方法并不受此注释的影响。

@Repeatable

表示注解可以重复使用,为了解决同一个注解不能重复在同一类/方法/属性上使用的问题。

自定义注解

注解的语法格式如下:

public @interface 注解名称 {
[访问级别修饰符] [数据类型] 属性名() default 默认值;
}
  • [访问级别修饰符]只能使用 public 或默认访问级别(即不指定访问级别修饰符)修饰。
  • [数据类型] 有限制要求。支持的数据类型如下:

    所有基本数据类型(byte、char、short、int、long、float、double、boolean)
    String 类型
    Class 类
    enum 类型
    Annotation 类型
    以上所有类型的数组

  • default 指定该属性的默认值,既然设置了属性,建议还是给出默认值。
  • 如果注解中只有一个属性值,最好将其命名为 value。因为,指定属性名为 value,在使用注解时,指定 value 的值可以不指定属性名称。如:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface TestClass {
    String value() default "haha";
}
@TestClass("aaa")
private final String mTestClass = "";
//或者
@TestClass(value = "aaa")
private final String mTestClass = "";

如果不是命名value,则不可以使用第一种方法,编译器会报错提示。

注解的使用

我们使用注解一般有两种使用方式:

  • 运行时处理注解:运用反射,比如GSON序列化
  • 编译期处理注解:使用注解处理器AbstractProcessor处理注解,然后通过javapoet生成一个新的java类。如:Arouter、Butterknife等
运行时注解

比如我现在有个数据类User,数据上传的时候需要把name转换成"T0",age转换成"T1"。

定义注解

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

User类

public class UserBean {
    @SerializedName("T0")
    private String name;
    @SerializedName("T1")
    private String age;
 
    public UserBean(String name, String age) {
        this.name = name;
        this.age = age;
    }

数据处理:

public class HandleJson {

    public String toJson(Object src) {
        JSONObject jsonObject = new JSONObject();
        //获得成员变量
        Field[] fields = src.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                if (field.getAnnotations() != null) {
                    //确定注解类型
                    if (field.isAnnotationPresent(SerializedName.class)) {
                        //允许修改反射属性
                        field.setAccessible(true);
                        SerializedName serializedName = field.getAnnotation(SerializedName.class);
                        String name = serializedName.value();
                        String value = (String) field.get(src);
                        jsonObject.put(name, value);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return jsonObject.toString();
    }
}

使用

val userBean: UserBean = UserBean("wzc", "22")
val handleJson = HandleJson()
val json = handleJson.toJson(userBean)
//输出的是{"T0":"wzc","T1":"22"}
编译时注解

可以看这篇文章:编译时注解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值