16 注解Annotation

16.1 基本Annotation

        JDK1.5开始,Java增加了对元数据的支持,也就是Annotation。Annotation必须使用工具来处理,工具负责提取Annotation里包含的元数据。

        元数据:修饰数据的数据,多个元数据包装在Annotation中修饰同一个数据。

        程序元素:类、构造器、方法、成员变量等都是程序元素。

        堆污染:把一个不带泛型的对象赋给一个带泛型的变量时,就会发生“堆污染”。在Java6及以前前,编译发生堆污染的方法,没有警告,但是调用发生堆污染的方法时,编译器就会发出警告;在Java7及以后,编译发生堆污染的方法一定会发出“堆污染”警告。

        函数式接口:接口中只有一个抽象方法的接口就是函数式接口,但可以包含多个默认方法和多个类方法。

    Java提供了5个基本的Annotation,位于java.lang包下:

        - @Override(限定重写父类方法):用来指定方法重载,强制一个子类必须覆盖父类的方法。借助此注解,编译器会在编译时进行严格的检查,避免子类中出现重写方法的方法名错误等问题。@Override注解只能修饰方法

        - @Deprecated(标示已过时):用于表示某个程序元素已过时,当其他程序使用这些元素时,编译器会给出警告。

        - @SuppressWarnings(抑制编译器警告):指示该Annotation修饰的元素取消显示指定的编译器警告,注解会从被注释的类一直作用到类里的方法和成员变量等。要在注解的括号内为value赋值,value的值为需要关闭的编译器警告。

        - @SafeVarargs(抑制堆污染警告):此注解Java7开始存在,发生堆污染的代码使用此注解后,编译时程序不会发出任何警告。

        - @FunctionInterface(函数式接口):此注解Java8开始存在,用来指定某个接口必须是函数式接口,通知编译器惊醒更严格的检查。@FunctionInterface只能修饰接口

16.2 元Annotation

        JDK在java.lang.annotation包下提供了6个元Annotation,其中有5个专门修饰Annotation定义。以下为4个常用的元注解。

16.2.1 @Retention

        @Retention只能修饰Annotation定义,用于指定被修饰的Annotation可以保留多长时间。

    @Retention的value成员变量值只能是如下三个之一:

        - RetentionPolicy.CLASS:编译器把注解记录在class文件中,运行时消失。这是默认值。

        - RetentionPolicy.RUNTIME:编译器把注解记录在class文件中,运行时JVM可以获取注解信息,程序可以通过反射获取注解信息。

        - RetentionPolicy.SOURCE:注解只保留在源代码中,编译器会直接丢弃这个Annotation。

16.2.2 @Target

        @Target只能修饰一个Annotation定义,用于指定被修饰的注解能用于修饰哪些程序单元。

    @Target的value成员变量值只能是如下几种: 

        - ElementType.ANNOTATION_TYPE:指定该策略的Annotation只能修饰Annotation。

        - ElementType.CONSTRUCTOR:指定该策略的Annotation只能修饰构造器。

        - ElementType.FIELD:指定该策略的Annotation只能修饰成员变量。

        - ElementType.LOCAL_VARIABLE:指定该策略的Annotation只能修饰局部变量。

        - ElementType.METHOD:指定该策略的Annotation只能修饰方法定义。

        - ElementType.PACKAGE:指定该策略的Annotation只能修饰包定义。

        - ElementType.PARAMETER:指定该策略的Annotation可以修饰参数。

        - Elementiype.TYPE:指定该策略的Annotation可以修饰类、接口(包括注解类型)或枚举定义。

16.2.3 @Documented

        @Documented修饰的注解会被javadoc工具提取到API文档中。

16.2.4 @Inherited

        @Inherited修饰的注解具有继承性。如果一个类使用了被@Inherited修饰的注解,则其子类将自动被那个注解修饰。

16.3 自定义Annotation

16.3.1 定义Annotation

        定义新的Annotation类型使用@interface关键字,与定义一个接口的方式相似。定义Annotation是可以带有成员变量,成员变量以抽象方法来声明,方法名和返回值类型分别定义了变量名和类型可以通过default为成员变量指定默认值。设定了默认值的成员变量可以在使用时不指定值,没有设置默认值的成员变量必须在使用注解时传入值。

        完整的定义过程:public @MyTag { String name(); default "wang" }

    根据Annotation中是否包含成员变量,吧Annotation分为两类:

        - 标记Annotation:没有定义成员变量的Annotation类型被称为标记。这种Annotation仅利用自身的存在与否来提供信息。如@Override和@Test等。

        - 元数据Annotation:包含成员变量的Annotation,因为可以接受元数据,所以被称为元数据Annotation。

16.3.2 提取Annotation信息

        使用@interface定义的注解,默认继承了Annotation接口。即Java使用Annotation接口来代表程序元素前面的注解,该接口是所有注解的父接口。

        Java5在java.lang.reflect包里新增的AnnotatedElement接口,是所有程序元素(Class、Constructor、Field、Method、Package)的父接口,程序可以通过反射获取某个类的AnnotatedElement对象,程序就可以调用如下方法访问Annotation信息

        - <A extends Annotation> A getAnnotation(Class<A> annotationClass):返回该程序上存在的、指定类型的注解,如果此类型注解不存在则返回null。

        - <A extendsAnnotation> A getDeclaredAnnotation(Class<A> annotationClass):Java8新增,该方法尝试获取直接修饰该程序元素、指定类型的Annotation。不存在返回null。

        - Annotation[] getAnnotations():返回该程序元素上存在的所有注解。

        - Annotation[] getDeclaredAnnotations():返回直接修饰该元素的所有Annotation。

        - boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否存在指定类型的注解,存在返回true,不存在返回false。

        - <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass):由于Java8增加了重复注解,所以需要使用此方法获取修饰该元素、指定类型的多个Annotation。

        - <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass):由于Java8增加了重复注解,所以需要使用此方法获取直接修饰该元素、指定类型的多个Annotation。

        如果需要获取某个注解里的元数据,则可以将注解强制类型转换为所需的注解类型,然后通过注解对象的抽象方法来访问这些元数据。如((MyTag) e).name();

        为了让注解起作用,必须为这些注解提供一个注解处理工具方法。

16.4 Java8新增的重复注解

        Java8以前,同一个程序元素只能使用一个相同类型的Annotation;如果需要在同一个元素前使用多个相同类型的Annotation,则必须使用Annotation容器。Java8以后允许使用多个相同类型的注解。开发重复注解需要使用@Repeatable修饰。注解容器保留期应该比重复注解的保留期要长

    定义举例:

        - @Repeatable(MyTags.class) public @interface MyTag { int age(); }

        - public @interface MyTags { MyTag[] value(); }

    使用方式:

        - 可以使用传统方式:@MyTags({@MyTag(age = 1), @MyTag(age = 2)})

        - 可以不使用容器包裹:@MyTag(age = 1)        @MyTag(age = 2)

        不使用容器包裹只是一种假象,当程序获取修饰某个程序元素的注解时,依旧能获取MyTags.class。

16.5 Java8新增的Type Annotation

        Java8为ElementType枚举新增了TYPE_PARAMETER、TYPE_USER两个枚举值,可以使用这两个枚举值为@Target赋值。使用@Target(ElementType.USE)修饰的注解被称为类型注解,类型注解可以在任何用到类型的地方出现。

    Java8以前注解只能出现在程序元素前,Java8以后注解也可以出现在任何用到类型的地方:

        - 创建对象,new @Nutnull Nemo();。

        - 类型转换,(@NotNull Nemo) nemo;。

        - 使用implement实现接口,implements @NotNull。

        - 使用throws声明抛出异常,throw @NotNull Exception;。

        目前Java8还未对类型注解提供检查框架。

16.6 编译时处理Annotation

        APT(Annotation Processing Tool)是一种注解处理工具,他对源代码文件进行检测,并找出文件所包含的Annotation信息。可以生成额外的源文件和其他文件用于存放注解信息,编译时需要使用-processor选项。        

        每个Annotation处理器都需要实现javax.annotation.processing包下的Processor接口,或者继承AbstractProcessor抽象类。

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

推荐阅读更多精彩内容

  • 本文章涉及代码已放到github上annotation-study 1.Annotation为何而来 What:A...
    zlcook阅读 28,923评论 15 116
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 你轻轻飘飘的来了,只为了要来提醒我一句:你可以偶尔翻篇,但别沉睡得太久。——————题记 夏天的气息真好,钟爱...
    漪叶子阅读 335评论 0 0
  • 要么读书,要么旅行,心灵和身体总有一个要在路上。去了成都,知道有那么一个你到了再也不想走的城市,和两个好朋友吃着火...
    huangxiaohao阅读 114评论 0 1
  • 倘若这是爱情 你大概会懂他的装扮 倘若这是爱情 你大概会懂他的心思 倘若这是爱情 你大概会理解他的行为 倘若这是爱...
    就是兔子先生阅读 204评论 1 0