java 注解
定义
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
注解就是和程序结构关联信息的标记,不影响程序运行。
功能
根据其功能分为:
编写文档:
通过代码里标识的元数据生成文档【生成文档doc文档】
代码分析:
通过代码里标识的元数据对代码进行分析【使用反射】
编译检查:
通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
注解代表了一种注解类型的特定调用,并且通常会提供该种类型的元素值。
总共有三种注解类型,如下:
- NormalAnnotation
- MarkerAnnotation
- SingleElementAnnotation
Normal Annotation
标准注解指定了一种注解类型的名称和可选的用逗号分开的元素-值对列表。每一个元素-值对都包含一个与注解类型关联元素的元素值。
声明方式如下:
NormalAnnotation:
@ TypeName ( [ElementValuePairList] )
ElementValuePairList:
ElementValuePair {, ElementValuePair}
ElementValuePair:
Identifier = ElementValue
ElementValue:
ConditionalExpression
ElementValueArrayInitializer
Annotation
ElementValueArrayInitializer:
{ [ElementValueList] [,] }
ElementValueList:
ElementValue {, ElementValue}
注意@是指向它自己的标志。可以在@和注解类型之间插入空格,但是不建议这么做。
TypeName指定了与这个注解关联的注解类型。就是属于哪个类型。
如果TypeName没有在注解的位置指定一个可访问的注解类型,将会产生编译时错误。
一个元素-值对中的Identifier必须是注解类型中的元素名称,否则会产生编译时错误。
这个方法的返回值类型定义了元素-值对的元素值类型。
如果这个元素类型是一个数组类型,不需要使用大括号来指定元素-值对中的元素值。如果元素值不是一个 ElementValueArrayInitializer,这个数组值,其唯一的元素就是这个元素关联的元素值。如果这个元素值是 ElementValueArrayInitializer,这个数组值代表了与这个元素关联的ElementValueArrayInitializer。
如果元素类型不与元素值对应,将会产生编译错误。当且仅当符合下面一种情况的时候,一个元素类型T对应于一个元素值V:
T是一个数组E[],或者:
- 如果V是一个ConditionalExpression或者一个Annotation,V对应于E
- 如果V是一个ElementValueArrayInitializer,V包含的每个元素值对应于E
除了ElementValueArrayInitializer可能包含语法注解以及表达式和嵌套初始化的情况下,ElementValueArrayInitializer类似于数组初始化。然而,ElementValueArrayInitializer中的嵌套初始化语法上并不合法,因为在注解类型的声明中从来没有对应于一个数组类型的元素(不允许嵌套数组类型)。
T不是数组类型,并且V的类型与T兼容,并且:
- 如果T是一个基本数据类型或者String,V是一个常量表达式
- 如果T是一个Class或者Class的引用,V是一个类名称.class
- 如果T是一个枚举类型,V就是一个枚举常量
- V非null
注意,如果T不是数组类型或者注解类型,元素值必须为一个ConditionalExpression(条件表达式)。条件表达式的使用不同于一般的表达式,它是一个语法技巧来避免赋值表达式作为元素值。因为赋值表达式不是常量表达式,它不可以对应基本数据类型或者String类型的元素。
形式上,将元素值作为FP-strict(float point 浮点精度)的说法是无效的,因为它可能是一个注解类型或者.class。我们可以将元素值作为FP-strict,当它是一个常量表达式或者一个常量表达式的数组或者一个值为常量表达式的注解,毕竟,每个常量表达式都是FP-strict表达式。
一个标准注解对于每个相对应的注解类型的元素必须包括一个元素-值对,否则将会产生编译时错误。
一个标准注解对于元素可能,并不一定需要,包含默认的元素-值对的值。
习惯上,尽管不需要,注解中的元素-值对与注解类型中声明的相应的元素具有相同的目的。
注解类型声明中的注解通常称为元注解。
T的注解类型可能作为元注解在声明类型T自身的时候。一般来说,注解的关系允许闭包圆形。
(例如,注解类型S使用T的元注解声明是可以的,并且为了注解T的自己的元注解S的声明。预定义注解类型包含几个这种闭环)
Marker Annotations
标记注解设计用于简化标记注解类型的使用。
MarkerAnnotation:
@ TypeName
它是标准注解的简化。
@TypeName()
对于注解类型,允许使用标记注解,就如同所有的元素都有默认的值。
Single-Element Annotations
单值注解,设计用来简化使用单值注解类型。
SingleElementAnnotation:
@ TypeName ( ElementValue )
是标准注解的简化。
@TypeName(value = ElementValue)
对于多个元素的注解,使用单值注解,相当于其他元素的值是默认值。
注解位置
声明注解就是应用于声明并且它的类型应用于声明所代表的的上下文环境的注解。
类型注解就是针对类型,并且适用于类型上下文的注解。
比如,成员变量的声明:
@Foo int f;
@Foo是一个对于f的声明注解,如果Foo是一个元注解通过@Target(ElementType.FIELD)指定,类型注解针对int,如果Foo是一个@Target(ElementType.TYPE_USE)指定的元注解。对于@Foo,既可能是一个声明注解也可能是一个类型注解。
类型注解可以适用于数组类型或者任何组件类型。比如,假如A,B,C是@Target(ElementType.TYPE_USE)指定的注解,给出成员的声明:
@C int @A [] @B [] f;
@A适用于int[][],@B适用于他的组件类型int[],@C用于元素类型int。
这个语法一个重要的属性就是,在两种声明之中唯一的不同就是数组维度的不同,左边类型的注解指向相同的类型,@C适用于int:
@C int f;
@C int[] f;
@C int[][] f;
习惯上,尽管不需要,在任何其他的修改之前书写声明注解,类型注解要在类型之前。
注解可能出现在程序的语法位置就是它可以确切适用于声明,或者类型,或者二者都有的地方。可能发生在这五种声明上下文中的任意一个情况,立即处理声明的属性的类型:
- Method declarations方法声明(包括注解类型元素)
- Constructor declarations构造方法声明
- Field declarations成员声明(包括枚举常量)
- Formal and exception parameter declarations正常和异常参数声明
- Local variable declarations本地变量声明(包括状态的循环变量和资源变量)
——-翻译—–
Multiple Annotations of the Same Type
如果没有声明注解为可复用,使用相同类型的多个注解会出现编译时异常。
Annotation Types
注解类型声明指定一个新的注解类型,一个特别的接口类型。为了区分注解类型声明和一般接口声明,使用@interface区分。
AnnotationTypeDeclaration:
{InterfaceModifier} @ interface Identifier AnnotationTypeBody
@interface是区分标志。可以使用空格将他们分开,但不是不建议这样做。
Identifier是声明注解类型的名称。
如果注解类型与它的内部类或者接口拥有相同的名称,将会出现编译时错误。
每个注解类型的直接父接口是:java.lang.annotation.Annotation
凭借注解类型声明语法,注解的声明并不是通用的,没有extends的情况是允许的。
Annotation Type Elements
AnnotationTypeBody:
{ {AnnotationTypeMemberDeclaration} }
AnnotationTypeMemberDeclaration:
AnnotationTypeElementDeclaration
ConstantDeclaration
ClassDeclaration
InterfaceDeclaration
;
AnnotationTypeElementDeclaration:
{AnnotationTypeElementModifier} UnannType Identifier ( ) [Dims] [DefaultValue] ;
AnnotationTypeElementModifier:
(one of)
Annotation public
abstract
Dims:
{Annotation} [ ] {{Annotation} [ ]}
注解声明的方法返回值类型如下,否则会出现编译时错误:
- 基本数据类型
- String
- Class或者.class
- 枚举类型
- 注解类型
- 以上数组类型
例子如下:
/**
* Describes the "request-for-enhancement" (RFE)
* that led to the presence of the annotated API element.
*/
@interface RequestForEnhancement {
int id(); // Unique ID number associated with RFE
String synopsis(); // Synopsis of RFE
String engineer(); // Name of engineer who implemented RFE
String date(); // Date RFE was implemented
}
/**
* An annotation with this type indicates that the
* specification of the annotated API element is
* preliminary and subject to change.
*/
@interface Preliminary {}
/**
* Associates a copyright notice with the annotated API element.
*/
@interface Copyright {
String value();
}
/**
* Associates a list of endorsers with the annotated class.
*/
@interface Endorsers {
String[] value();
}
interface Formatter {}
// Designates a formatter to pretty-print the annotated class
@interface PrettyPrinter {
Class<? extends Formatter> value();
}
/**
* Indicates the author of the annotated program element.
*/
@interface Author {
Name value();
}
/**
* A person's name. This annotation type is not designed
* to be used directly to annotate program elements, but to
* define elements of other annotation types.
*/
@interface Name {
String first();
String last();
}
@interface Quality {
enum Level { BAD, INDIFFERENT, GOOD }
Level value();
}
Defaults for Annotation Type Elements
DefaultValue:
default ElementValue
例子如下:
@interface RequestForEnhancementDefault {
int id(); // No default - must be specified in
// each annotation
String synopsis(); // No default - must be specified in
// each annotation
String engineer() default "[unassigned]";
String date() default "[unimplemented]";
}
Repeatable Annotation Types
如果一个注解需要复用需要@Repeatable声明。
例子:
@Repeatable(FooContainer.class)
@interface Foo {}
@interface FooContainer { Object[] value(); }
@Target(ElementType.TYPE)
@Repeatable(FooContainer.class)
@interface Foo {}
@Target(ElementType.ANNOTATION_TYPE)
@Interface FooContainer {
Foo[] value();
}
// Foo: Repeatable annotation type
@Repeatable(FooContainer.class)
@interface Foo { int value(); }
// FooContainer: Containing annotation type of Foo
// Also a repeatable annotation type itself
@Repeatable(FooContainerContainer.class)
@interface FooContainer { Foo[] value(); }
// FooContainerContainer: Containing annotation type of FooContainer
@interface FooContainerContainer { FooContainer[] value(); }
@FooContainer({@Foo(1)}) @FooContainer({@Foo(2)})
class A {}
Predefined Annotation Types预定义注解类型
@Target
java.lang.annotation的注解类型的一种。Target用来声明注解类型,来指定注解适用的上下文环境。Target有单元素,值,java.lang.annotation.ElementType[]类型,来指定上下文环境。
注解类型可能适用于上下文的声明。
有八种声明的上下文环境,每个对应于java.lang.annotation.ElementType的枚举常量:
Package声明
对应于 java.lang.annotation.ElementType.PACKAGE
类型声明:class,interface,enum,和注解类型
对应于java.lang.annotation.ElementType.TYPE
此外注解类型对应于java.lang.annotation.ElementType.ANNOTATION_TYPE
方法声明(包括注解类型的元素)
对应于java.lang.annotation.ElementType.METHOD
构造函数声明
对应于 java.lang.annotation.ElementType.CONSTRUCTOR
类型参数声明,class,interface,methods,constructs
对应于 java.lang.annotation.ElementType.PARAMETER
成员声明
对应于 java.lang.annotation.ElementType.FIELD
正常和异常参数声明
对应于 java.lang.annotation.ElementType.PARAMETER
本地变量声明
对应于 java.lang.annotation.ElementType.LOCAL_VARIABLE
@Retention
只在源代码中存在,或者存在于二进制文件中。
定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在运行时有效(即运行时保留)
例子:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
@Documented
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
例子:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
@Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
实例代码:
/**
*
* @author peida
*
*/
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
@Override
重写方法注解
@SuppressWarnings
@Deprecated
@SafeVarargs
@Repeatable
@FunctionalInterface
Demo
根据上面的注解内容,写了一个Android常用的View注解框架,使用方便,简单,参见View Inject