Java注解,小试牛刀

本文介绍了Java注解的基础知识,包括元注解RetentionPolicy和ElementType的策略,以及注解的属性使用。通过实例展示了SOURCE、CLASS、RUNTIME策略的注解在实际编程中的应用,适合Java初学者学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引子

Android开发的话一般都用过ButterKnife 、Retrofit之流,毕竟都是超流行的框架;

本想对这些东西做个使用小结,但是发现里面用的Java注解本人还挺模糊,所以先决定看一下Java注解这一块;

这文章适合对Java注解一窍不通的小伙伴一起学习;

Java注解Annotation

名词解释也不知道怎么说,先来看看样子。使用Android Studio开发的话,可以直接new一个java注解出来;

路径大概是:new->Java Class->kind中选择Annotation;

随便起个名字,比如MyAnnotation,点击OK就可以创建自己的注解了。

public @interface MyAnnotation {
}

这里可以看到,@interface这个标签,姑且就叫它标签好了;类似class代表类,interface代表接口一样,@interface这个标签代表注解;

下面先看一个真正的,有用的注解,ButterKnife中的BindView

package butterknife;

import android.support.annotation.IdRes;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.CLASS;

/**
 * Bind a field to the view for the specified ID. The view will automatically be cast to the field
 * type.
 * <pre><code>
 * {@literal @}BindView(R.id.title) TextView title;
 * </code></pre>
 */
@Retention(CLASS) @Target(FIELD)
public @interface BindView {
  /** View ID to which the field will be bound. */
  @IdRes int value();
}

emm...看着很棒,除了@interface之外,还有这么多@XXX;这里先看下@Retention和@Target,这是在注解中的特殊存在——元注解;

元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面;

这里先大概介绍几个元注解;

名称使用描述
RetentionPolicy@Retention保留策略,是一个枚举类型,用来决定保留多长时间
ElementType@Target此枚举类型的常量提供了注释可能出现在Java程序中的语法位置的简单分类
Document@Documented表示默认情况下,javadoc和类似工具将记录带有类型的注释。 此类型应用于注释类型的声明,其注释会影响客户端对带注释元素的使用。 如果使用Documented注释类型声明,则其注释将成为带注释元素的公共API的一部分。
Inherited@Inherited表示自动继承注释类型。 如果注释类型声明中存在Inherited元注释,并且用户在类声明上查询注释类型,并且类声明没有此类型的注释,则将自动查询类的超类以获取注释类型。 将重复此过程,直到找到此类型的注释,或者到达类层次结构(对象)的顶部。 如果没有超类具有此类型的注释,则查询将指示相关类没有此类注释。

RetentionPolicy

分为3种策略:SOURCE、CLASS、RUNTIME,一个注解只能有一种策略;

  • SOURCE:编译器将丢弃注释。也就是说注释只在编译器处理期间器作用,编译完就没用了;对应Java源文件(.java文件);
  • CLASS:注释将由编译器记录在class文件中,但在运行时不需要由VM保留。 这是默认行为;对应.class文件;
  • RUNTIME:注释将由编译器记录在class文件中,并在运行时由VM保留,因此可以反射性地读取它们;对应内存中的字节码;

ElementType

一共有10种类型,一个注解的类型可以有多个;

  • TYPE:类、接口(包括注释类型)或枚举声明;
  • FIELD:字段声明(包括枚举常量);
  • METHOD:方法声明;
  • PARAMETER:参数声明;
  • CONSTRUCTOR:构造方法声明;
  • LOCAL_VARIABLE:局部变量声明;
  • ANNOTATION_TYPE:注释类型声明,元注解用的就是这个声明;
  • PACKAGE:包声明;

在 Java 8 之前的版本中,只能允许在声明式前使用 Annotation。而在 Java 8 版本中,Annotation 可以被用在任何使用 Type 的地方,例如:初始化对象时 (new),对象类型转化时,使用 implements 表达式时,或者使用 throws 表达式时。

  • TYPE_PARAMETER:JDK1.8新增, Type parameter declaration,表示这个 Annotation 可以用在 Type 的声明式前;
  • TYPE_USE:JDK1.8新增,Use of a type,表示这个 Annotation 可以用在所有使用 Type 的地方(如:泛型,类型转换等);

再回头看BindView的代码,它使用的是@Retention(CLASS) @Target(FIELD),代表注释将记录在class中,注释的类型是字段,具体使用时如下:

@BindView(R.id.id_my_list)
RecyclerView myList;

注解的属性

BindView的代码中,BindView还有一行代码@IdRes int value();这部分是注解的属性;

注解的属性是可选的,可以带属性,也可以不带任何属性;

无属性注解

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface MyAnnotation {
}

带属性注解

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface MyAnnotation {
    String name() default "Michael Jordan";
    int age();
}

属性是可以用default设置默认值的;

属性的赋值方式是在注解的括号内以 value=”” 形式,多个属性之前用 ,隔开;

@MyAnnotation(name = "GG", age = 10)
View btn1;

当然,带有default值的属性可以不被赋值,因此上面的代码写成如下形式,编译上依然不会有问题;

@MyAnnotation(age = 10)
View btn1;

但如果把age属性的赋值也去掉,那就出报错了;

还有一个特殊的属性value,当只对一个属性进行赋值的时候value可以省略不写;

public @interface MyAnnotation {
    int value();
}


@MyAnnotation(10)
View btn1;

等同于

@MyAnnotation(value = 10)
View btn1;

这时候再回看ButterKnife中BindView的代码,它就是直接用的value做属性,所以在赋值时直接写对应的值就可以了;

注解的具体使用

SOURCE策略注解的使用

在控制输入参数时,我们常常用到枚举类型,这里可以用SOURCE类型的注释起到类似的效果;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PARAMETER)
@IntDef({MyTestAnnotation.STATE_ON, MyTestAnnotation.STATE_OFF})
public @interface MyAnnotation {
}



public class MyTestAnnotation {
    public static final int  STATE_ON = 1;
    public static final int  STATE_OFF = 2;

    int mState = STATE_OFF;

    public void setState(@MyAnnotation() int state) {
        mState = state;
    }

    public int getState() {
        return mState;
    }
}

可以成功限制输入的内容;

@IntDef也是一种元注解,它是属于android的,源码如下:

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.ANNOTATION_TYPE})
public @interface IntDef {
    int[] value() default {};

    boolean flag() default false;
}

CLASS策略注解的使用

BufferKnife就是此类型的注解;

这里涉及到annotationProcessorJavaPoet,相对比较复杂,这里有篇文章介绍相对详细:

https://www.jianshu.com/p/39fc66aa3297

RUNTIME策略注解的使用

这里通常是通过反射机制;

先定义两个注解,其中MyAnnotation包含MyInternalAnnotation;

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


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface MyAnnotation {
    String name();
    int age() default 0;
    int[] arrayAttr() default {3, 6, 5};
    MyInternalAnnotation annotationAttr() default @MyInternalAnnotation("Good");
}

再通过JAVA文件使用,千言万语都在代码中;

public class MyTestAnnotation {
    String TAG = "mYtaG";

    @MyAnnotation(name = "My Name")
    String testString = "";

    public void test() {
        //检查是否存在注解
        if (testString.getClass().isAnnotationPresent(MyAnnotation.class)) {
            //获取注解
            MyAnnotation annotation = testString.getClass().getAnnotation(MyAnnotation.class);
            Log.v(TAG, "name:" +annotation.name());
            Log.v(TAG, "age:" +annotation.age());
            Log.v(TAG, "array-length:" +annotation.arrayAttr().length);

            //获取子注解
            MyInternalAnnotation internalAnnotation = annotation.annotationAttr();
            Log.v(TAG, "internal value:" +internalAnnotation.value());
        }

    }
}

PS:属性不止可以用int和String还可以用枚举类型啥的;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值