什么是Java Annotation
Java语言规范中对Annotation的说明如下:
An annotation is a marker which associates information with a program construct, but has no effect at run time. An annotation denotes a specific invocation of an annotation type and usually provides values for the elements of that type. — Annotations
在Java™ Tutorials中也有较为详细的说明,内容如下
注解是元数据的一种形式,它提供了关于一个程序的数据但它并不是程序本身的组成部分。注解对被注解的代码执行没有任何直接影响。
注解通常有如下几个用途:
➣ 为编译器提供信息 — 注解可以被编译器用来检查错误或者禁止显示警告(suppress warnings)
➣ 编译时和部署时处理 — 软件工具可以通过处理注解信息来生成代码、XML文件等
➣ 运行时处理 — 一些注解可以在运行时被检查
使用注解以@开头来告诉编译器接下来是一个注解,注解中可以有命名或者未命名的元素信息,通常当注解只有一个元素的时候我们可以省略名称
注解的格式
最简单的注解格式看起来像下面这样:1
@符号向编译器指明接来下是一个注解,下面的注解的大家应该都不陌生,注解名是Override:1
2
void mySuperMethod() { ... }
注解可以包含元素,可以是有名称的和没有名称的,并且这些元素都有值:1
2
3
4
5 (
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass() { ... }
或者像下面这个注解1
2"unchecked") (value =
void myMethod() { ... }
当只有一个元素叫value的时候,name可以省略,如下:1
2"unchecked") (
void myMethod() { ... }
如果注解没有元素的话,括号也可以被省略,就像上面的@Override
我们也可以在同一个声明中使用多注解:1
2
3"Jane Doe") (name =
class MyClass { ... }
如果注解具有相同的类型,我们叫“重复注解”,重复注解在Java SE 8中支持(Repeating Annotations)。注解类型可以是java.lang 或 java.lang.annotation包中定义的,上面的例子中 Override 和 SuppressWarnings是预定义的注解,我们也可以自定义注解,上面例子中Author 和 Ebook 是自定注解。
注解可以被用在什么地方
注解可以被用于声明:类、字段、方法和其它程序元素上。在Java SE 8 release中,注解也可以被用于类的使用上,如类的实例创建语句、类型转化、类继承以及抛出异常声明,这中形式的注解叫a type annotation。
预定义注解
在Java SE API中预定义了一些注解,其中一些注解在Java编译器中被使用,也有一些注解适用于其它注解。Android中也有很多预定义注解,如常用的android.support.annotation包中预定义的注解,通常用于辅助性的代码检查。
Java语言中使用的注解类型
这类预定义注解在java.lang包中,如下:
➣ @Deprecated表示被标记的元素过时并且不应该再被使用。当一个程序使用了 @Deprecated 标记的方法、类或者变量时,编译器会生成一个警告。当一个元素过时了,我们也应该在文档注释中使用Javadoc的@deprecated标签。文档注释中的@符号和注解中的@符号在概念上是相关的,并且一个以小写开头,一个以大写开头,如下:1
2
3
4
5
6
7
8 // Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated
*/
static void deprecatedMethod() { }
}
➣ @Override告诉编译器该元素会覆盖父类中声明的对应元素,也就是方法的重写。当我们进行方法重写的时候,这个注解并不是必须的,但能帮助我们避免错误,当一个方法被@Override标记,但没有正确覆写,如方法名拼错的时候,编译器会报错,用法如下:1
2
3
4// mark method as a superclass method
// that has been overridden
int overriddenMethod() { }
➣ @SuppressWarnings告诉编译器去除将会生成的指定的警告。下面的例子中一个过时的方法被使用,通常编译器会生成一个警告,在这个例子中,@SuppressWarnings注解会抑制警告,如下:1
2
3
4
5
6
7
8// use a deprecated method and tell
// compiler not to generate a warning
"deprecation") (
void useDeprecatedMethod() {
// deprecation warning
// - suppressed
objectOne.deprecatedMethod();
}
每个编译器警告都属于一个类别。Java语言规范中列出了两类:deprecation
和unchecked
。unchecked
警告在我们使用泛型可能会遇到,要想抑制多个类别的警告,可以使用下面的语法:1
"unchecked", "deprecation"}) ({
➣ @SafeVarargs在Java SE 7中引入(以上三个在Java SE 5中也就是1.5中引入),当被用于一个方法或者构造方法时,断言了代码中的可变参数不会产生潜在的不安全操作。当这个注解被使用的时候,可变参数相关的unchecked
警告会被抑制。
➣ @FunctionalInterface在Java SE 8中引入,表明类声明为一个函数式接口,该注解只能标记在有且仅有一个抽象方法的接口上(JDK8中interface中的static
方法和default
方法不算抽象方法)。该注解不是必须的,如果一个接口符合“函数式”接口定义,可以不加,加上后能够让编译器对函数式接口进行检查。我们常用的一些接口Callable
、Runnable
、Comparator
等在JDK8中都添加了@FunctionalInterface注解。语言设计者投入了大量精力来思考如何使现有的函数友好地支持lambda,最终采取的方法是:增加函数式接口的概念。。
适用于其它注解上的注解
适用于其它注解上的注解,叫元注解 (meta-annotations),在java.lang.annotation
包中定义了如下几个元注解:
➣ @Retention指明了标记注解保留的时间,如果在注解类声明中没有Retention注解,默认保留策略为RetentionPolicy.CLASS。
● RetentionPolicy.SOURCE
:source级别注解,编译时将会被忽略
● RetentionPolicy.CLASS
:编译时注解,会被编译器保留,但在JVM中会被忽略
● RetentionPolicy.RUNTIME
:运行时注解,会被JVM保留
➣ @Documented表明无论什么时候使用被@Document注解声明的注解,被使用的元素都应该被Javadoc工具文档化(默认情况下注解不在Javadoc中),也就是说注解类型信息也会被包括在生成的Javadoc文档中。通常我们使用@Document来替代代码中格式相同的注释。
➣ @Target表明被标记的注解被使用的限定范围,目标注解指定下列元素类型作为其值:
● ElementType.ANNOTATION_TYPE
:可以被用于一个注解类上
● ElementType.CONSTRUCTOR
:表明注解用于构造方法中
● ElementType.FIELD
:表明注解用于成员变量或者属性中
● ElementType.LOCAL_VARIABLE
:表明注解用于局部变量
● ElementType.METHOD
:表明注解用于方法
● ElementType.PACKAGE
:表明注解用于包名声明上
● ElementType.PARAMETER
:表明注解用于方法的参数
● ElementType.TYPE
表明注解用于类元素上
➣ @Inherited表明注解类可以从父类继承(默认不可以)。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类,也就是通过子类可以获取到父类的相应注解信息。
➣ @Repeatable Java SE 8中引入,表明标记的注解可以被同一个声明或者类使用多次,详细信息可以查看Repeating Annotations。
Android中预定义的注解
注解在android中是非常常见的,在官网搜索annotation会出现如下结果:1
2
3
4
5
6android.annotation
android.support.annotation
android.support.test.annotation
android.test.suitebuilder.annotation
android.text.Annotation
......
这里说下android.support.annotation包中提供的注解,该包中的注解类型可以分为三类:
➣ Nullness注解:如使用@NonNull
注解修饰的参数不能为null,使用@Nullable
注解修饰的函数参数或者返回值可以为null
➣ 资源类型注解:如可以使用@StringRes
限定方法中int类型参数需要为String资源id
➣ Def类注解:基于Intellij的“魔术常量”检查机制,如:可以使用@IntDef
限定方法中形式参数为指定的int常量值,以此代替枚举类型,同样可以使用@StringDef
限定形式参数为指定的String常量值。