Java中的注解

什么是注解

Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.

注解是一种元数据,提供代码的信息,但不是代码的一部分,对被注解的代码的运行没有影响。

这里提到的元数据是描述数据的数据,结合实例来说明:

<string name="app_name">AnnotionDemo</string>

这里的"app_name"就是描述数据"AnnotionDemo"的数据。

public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.tv)
    TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Views.inject(this);  // 利用反射获取注解的信息
    }
}

注解的作用

Annotations have a number of uses, among them:

  • Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.

  • Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.

  • Runtime processing — Some annotations are available to be examined at runtime.

  • 为编译器提供信息 —— 比如被 @Override 标记的方法如果不是父类的某个方法,IDE 会报错

  • 编译时和部署时处理 —— 软件工具可以使用注解信息生成代码、XML文件等

  • 运行时处理 —— 比如第三方框架xUtils,通过注解 @ViewInject 减少对 findViewById 的调用

注解是如何工作的?

注解仅仅是元数据,和业务逻辑无关,所以当你查看注解类时,发现里面没有任何逻辑处理

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

    int value();
}

如果注解不包含业务逻辑处理,必然有人来实现这些逻辑。注解的逻辑实现是元数据的用户来处理的,注解值提供信息,由注解的用户读取这些信息并实现必要的逻辑。

当使用java中的注解时(比如@Override、@Deprecated、@SuppressWarnings)JVM就是用户。

如果是自定义的注解,它的用户是每个使用注解的类,在被注解的类中通过反射来获取注注解的信息。

比如使用注解简化 findViewById 的操作

public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.tv)
    TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Views.inject(this);  // 利用反射获取注解的信息
    }
}

Views.inject(this) 的实现

public class Views {

    public static void inject(Activity activity) {
        Class<? extends Activity> activityClass = activity.getClass();
        Field[] fields = activityClass.getDeclaredFields();
        for (Field field : fields) {
            InjectView injectViewAnnotation = field.getAnnotation(InjectView.class);
            if (injectViewAnnotation != null) {
                View view = activity.findViewById(injectViewAnnotation.value());
                try {
                    field.setAccessible(true);
                    field.set(activity, view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注解的元注解

通过阅读注解类的源码可以发现,任何一个注解类都有如下特征:

  • 注解类会被 @interface 标记
  • 注解类的顶部会被 @Documented、@Retention、@Target、@Inherited这四个注解标记(@Target必须要有,其他可选)

@Target

@Target annotation marks another annotation to restrict what kind of Java elements the annotation can be applied to.

@Target 用来约束该注解的使用范围。主要有几个

ElementType.ANNOTATION_TYPE can be applied to an annotation type.
ElementType.CONSTRUCTOR can be applied to a constructor.
ElementType.FIELD can be applied to a field or property.
ElementType.LOCAL_VARIABLE can be applied to a local variable.
ElementType.METHOD can be applied to a method-level annotation.
ElementType.PACKAGE can be applied to a package declaration.
ElementType.PARAMETER can be applied to the parameters of a method.
ElementType.TYPE can be applied to any element of a class.

  • CONSTRUCTOR 构造函数
  • FIELD 实例变量
  • LOCAL_VARIABLE 局部变量
  • METHOD 方法
  • PACKAGE 包
  • PARAMETER 参数
  • TYPE 用于描述类、接口(包括注解类型) 或enum声明

@Retention

@Retention @Retention annotation specifies how the marked annotation is stored:

  • RetentionPolicy.SOURCE – The marked annotation is retained only in the source level and is ignored by the compiler.
  • RetentionPolicy.CLASS – The marked annotation is retained by the compiler at compile time, but is ignored by the Java Virtual Machine (JVM).
  • RetentionPolicy.RUNTIME – The marked annotation is retained by the JVM so it can be used by the runtime environment.

表示需要在什么级别保存该注解信息,用于描述注解的生命周期,即被描述的注解在什么范围内有效

  • SOURCE 在源文件中有效,即源文件保留;
  • CLASS 在class文件中有效,即 class 保留;
  • RUNTIME 在运行时有效,即运行时保留;

@Documented

@Documented annotation indicates that whenever the specified annotation is used those elements should be documented using the Javadoc tool. (By default, annotations are not included in Javadoc.)

可以用来生成 Javadoc 文档

@Inherited

@Inherited annotation indicates that the annotation type can be inherited from the super class. (This is not true by default.) When the user queries the annotation type and the class has no annotation for this type, the class' superclass is queried for the annotation type. This annotation applies only to class declarations.

用于描述某个被标注的类型是可被继承的

自定义注解

格式

元注解
public @interface 注解名 {
    定义体;
}

特别说明

  1. 注解类中的方法只能用 public 或者默认这两个访问权修饰,不写public就是默认
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    public enum Color{ BULE,RED,GREEN};
    Color fruitColor() default Color.GREEN;
}
···

2. 如果注解类中只有一个成员,最好把方法名设置为"value",比如

```java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}
  1. 注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为 null。因此, 使用空字符串或 0 作为默认值是一种常用的做法。

实战

自定义注解 ToDo.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
    public enum Priority {LOW, MEDIUM, HIGH}
    public enum Status {STARTED, NOT_STARTED}    
    String author() default "Yash";
    Priority priority() default Priority.LOW;
    Status status() default Status.NOT_STARTED;
}

使用注解的类 BusinessLogic

public class BusinessLogic {
    public BusinessLogic() {
        super();
    }

    public void compltedMethod() {
        System.out.println("This method is complete");
    }    

    @Todo(priority = Todo.Priority.HIGH)
    public void notYetStartedMethod() {
        // No Code Written yet
    }

    @Todo(priority = Todo.Priority.MEDIUM, author = "Uday", status = Todo.Status.STARTED)
    public void incompleteMethod1() {
        //Some business logic is written
        //But its not complete yet
    }

    @Todo(priority = Todo.Priority.LOW, status = Todo.Status.STARTED )
    public void incompleteMethod2() {
        //Some business logic is written
        //But its not complete yet
    }
}

注解的用户 TodoReport

public class TodoReport {
    public TodoReport() {
        super();
    }

    public static void main(String[] args) {
        getTodoReportForBusinessLogic();
    }

    /**
     * 解析使用注解的类,获取通过注解设置的属性
     */
    private static void getTodoReportForBusinessLogic() {
        Class businessLogicClass = BusinessLogic.class;
        for(Method method : businessLogicClass.getMethods()) {
            Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
            if(todoAnnotation != null) {
                System.out.println(" Method Name : " + method.getName());
                System.out.println(" Author : " + todoAnnotation.author());
                System.out.println(" Priority : " + todoAnnotation.priority());
                System.out.println(" Status : " + todoAnnotation.status());
                System.out.println(" --------------------------- ");
            }
        }
    }
}

运行结果

Method Name : notYetStartedMethd
Author : Yash
Priority : HIGH
Status : NOT_STARTED
--------------------------
Method Name : incompleteMethod2
Author : Yash
Priority : LOW
Status : STARTED
--------------------------
Method Name : incompleteMethd1
Author : Uday
Priority : MIDUM
Status : STARTED
--------------------------

总结

  • 注解是一种元数据
  • 注解的作用
  • 注解和业务逻辑无关,需要有个类来读取注解的信息
  • 注解的元注解
  • 如何实现自定义的注解

参考来源

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容