Annotation系列之基础篇

什么是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
@Entity

@符号向编译器指明接来下是一个注解,下面的注解的大家应该都不陌生,注解名是Override:

1
2
@Override
void mySuperMethod() { ... }

注解可以包含元素,可以是有名称的和没有名称的,并且这些元素都有值:

1
2
3
4
5
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass() { ... }

或者像下面这个注解

1
2
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }

当只有一个元素叫value的时候,name可以省略,如下:

1
2
@SuppressWarnings("unchecked")
void myMethod() { ... }

如果注解没有元素的话,括号也可以被省略,就像上面的@Override
我们也可以在同一个声明中使用多注解:

1
2
3
@Author(name = "Jane Doe")
@EBook
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
*/
@Deprecated
static void deprecatedMethod() { }
}

@Override告诉编译器该元素会覆盖父类中声明的对应元素,也就是方法的重写。当我们进行方法重写的时候,这个注解并不是必须的,但能帮助我们避免错误,当一个方法被@Override标记,但没有正确覆写,如方法名拼错的时候,编译器会报错,用法如下:

1
2
3
4
// mark method as a superclass method
// that has been overridden
@Override
int overriddenMethod() { }

@SuppressWarnings告诉编译器去除将会生成的指定的警告。下面的例子中一个过时的方法被使用,通常编译器会生成一个警告,在这个例子中,@SuppressWarnings注解会抑制警告,如下:

1
2
3
4
5
6
7
8
// use a deprecated method and tell 
// compiler not to generate a warning
@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
// deprecation warning
// - suppressed
objectOne.deprecatedMethod();
}

  每个编译器警告都属于一个类别。Java语言规范中列出了两类:deprecationuncheckedunchecked警告在我们使用泛型可能会遇到,要想抑制多个类别的警告,可以使用下面的语法:

1
@SuppressWarnings({"unchecked", "deprecation"})

@SafeVarargs在Java SE 7中引入(以上三个在Java SE 5中也就是1.5中引入),当被用于一个方法或者构造方法时,断言了代码中的可变参数不会产生潜在的不安全操作。当这个注解被使用的时候,可变参数相关的unchecked警告会被抑制。
@FunctionalInterface在Java SE 8中引入,表明类声明为一个函数式接口,该注解只能标记在有且仅有一个抽象方法的接口上(JDK8中interface中的static方法和default方法不算抽象方法)。该注解不是必须的,如果一个接口符合“函数式”接口定义,可以不加,加上后能够让编译器对函数式接口进行检查。我们常用的一些接口CallableRunnableComparator等在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
6
android.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常量值。

文章目录
  1. 什么是Java Annotation
    1. 注解的格式
    2. 注解可以被用在什么地方
  2. 预定义注解
    1. Java语言中使用的注解类型
    2. 适用于其它注解上的注解
    3. Android中预定义的注解
|