Overview of Java Built-in Annotations – Java内置注解的概述

最后修改: 2017年 12月 31日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll talk about a core feature of the Java language – the default annotations available in the JDK.

在这篇文章中,我们将讨论Java语言的一个核心特征–JDK中可用的默认注解。

2. What an Annotation Is

2.什么是注释

Simply put, annotations are Java types that are preceded by an “@” symbol.

简单地说,注解是Java类型,前面有一个”@”符号

Java has had annotations ever since the 1.5 release. Since then, they’ve shaped the way we’ve designed our applications.

从1.5版本开始,Java就有了注解。从那时起,它们就塑造了我们设计应用程序的方式。

Spring and Hibernate are great examples of frameworks that rely heavily on annotations to enable various design techniques.

Spring和Hibernate是很好的框架例子,它们严重依赖注解来实现各种设计技术。

Basically, an annotation assigns extra metadata to the source code it’s bound to. By adding an annotation to a method, interface, class, or field, we can:

基本上,注解将额外的元数据分配给它所绑定的源代码。通过向一个方法、接口、类或字段添加注解,我们可以。

  1. Inform the compiler about warnings and errors
  2. Manipulate source code at compilation time
  3. Modify or examine behavior at runtime

3. Java Built-in Annotations

3.Java内置注解

Now that we’ve reviewed the basics, let’s take a look at some annotations that ship with core Java. First, there are several that inform compilation:

现在我们已经回顾了基础知识,让我们来看看核心Java中的一些注解。首先,有几个注释为编译提供信息。

  1. @Override
  2. @SuppressWarnings
  3. @Deprecated
  4. @SafeVarargs
  5. @FunctionalInterface
  6. @Native

These annotations generate or suppress compiler warnings and errors. Applying them consistently is often a good practice since adding them can prevent future programmer error.

这些注释产生或抑制了编译器的警告和错误。始终如一地应用它们通常是一种好的做法,因为添加它们可以防止未来的程序员错误。

The @Override annotation is used to indicate that a method overrides or replaces the behavior of an inherited method.

@Override注解被用来表示一个方法覆盖或替换一个继承方法的行为。

@SuppressWarnings indicates we want to ignore certain warnings from a part of the code. The @SafeVarargs annotation also acts on a type of warning related to using varargs.

@SuppressWarnings表示我们要忽略代码中某个部分的某些警告。@SafeVarargs注解也作用于一种与使用varargs有关的警告。

The @Deprecated annotation can be used to mark an API as not intended for use anymore. Moreover, this annotation has been retrofitted in Java 9 to represent more information about the deprecation.

@Deprecated注解可用于将某个API标记为不打算再使用。此外,在Java 9中,该注解已被改装,以表示有关废弃的更多信息。

For all these, you can find more detailed information in the articles linked.

对于所有这些,你可以在链接的文章中找到更详细的信息。

3.1. @FunctionalInterface

3.1.@FunctionalInterface

Java 8 allows us to write code in a more functional way.

Java 8允许我们以一种更实用的方式编写代码。

Single Abstract Method interfaces are a big part of this. If we intend a SAM interface to be used by lambdas, we can optionally mark it as such with @FunctionalInterface:

单一抽象方法接口是其中的一个重要部分。如果我们打算让一个SAM接口被lambdas使用,我们可以选择用@FunctionalInterface来标记它。

@FunctionalInterface
public interface Adder {
    int add(int a, int b);
}

Like @Override with methods, @FunctionalInterface declares our intentions with Adder.

@Override与方法一样,@FunctionalInterfaceAdder来声明我们的意图。

Now, whether we use @FunctionalInterface or not, we can still use Adder in the same way:

现在,无论我们是否使用@FunctionalInterface,我们仍然可以以同样的方式使用Adder

Adder adder = (a,b) -> a + b;
int result = adder.add(4,5);

But, if we add a second method to Adder, then the compiler will complain:

但是,如果我们给Adder添加第二个方法,那么编译器会抱怨。

@FunctionalInterface
public interface Adder { 
    // compiler complains that the interface is not a SAM
    
    int add(int a, int b);
    int div(int a, int b);
}

Now, this would’ve compiled without the @FunctionalInterface annotation. So, what does it give us?

现在,如果没有@FunctionalInterface注解,这就已经编译了。那么,它给了我们什么?

Like @Override, this annotation protects us against future programmer error. Even though it’s legal to have more than one method on an interface, it isn’t when that interface is being used as a lambda target. Without this annotation, the compiler would break in the dozens of places where Adder was used as a lambda. Now, it just breaks in Adder itself.

@Override一样,这个注解可以保护我们免受未来程序员的错误。尽管在一个接口上有多个方法是合法的,但当该接口被用作 lambda 目标时就不合法了。如果没有这个注释,编译器会在Adder被用作λ的几十个地方中断。现在,它只是在Adder 本身中发生故障。

3.2. @Native

3.2.@Native

As of Java 8, there is a new annotation in the java.lang.annotation package called NativeThe @Native annotation is only applicable to fields. It indicates the annotated field is a constant that may be referenced from the native code. For instance, here’s how it’s used in the Integer class:

从 Java 8 开始,在 java.lang.annotation 包中有一个新的注解,叫做 Native@Native注解仅适用于字段。它表示被注解的字段是一个常量,可以从本地代码中引用。例如,下面是它在Integer类中的使用方法。

public final class Integer {
    @Native public static final int MIN_VALUE = 0x80000000;
    // omitted
}

This annotation can also serve as a hint for the tools to generate some auxiliary header files.

这个注释也可以作为工具的提示,生成一些辅助的头文件。

4. Meta-Annotations

4.元注释

Next, meta-annotations are annotations that can be applied to other annotations.

接下来,元注释是可以应用于其他注释的注释。

For example, these meta-annotations are used for annotation configuration:

例如,这些元注解被用于注解配置。

  1. @Target
  2. @Retention
  3. @Inherited
  4. @Documented
  5. @Repeatable

4.1. @Target

4.1.@Target

The scope of annotations can vary based on the requirements. While one annotation is only used with methods, another annotation can be consumed with constructor and field declarations.

注释的范围可以根据需求而变化。当一个注解只用于方法时,另一个注解可以被用于构造函数和字段声明。

To determine the target elements of a custom annotation, we need to label it with a @Target annotation.

为了确定自定义注解的目标元素,我们需要用@Target注解来标记它。

@Target can work with 12 different element types. If we look at the source code of @SafeVarargs, then we can see that it must be only attached to constructors or methods:

@Target可以与12种不同的元素类型一起工作。如果我们看一下@SafeVargs的源代码,那么我们可以看到它必须只连接到构造函数或方法。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {
}

4.2. @Retention

4.2.@Retention

Some annotations are meant to be used as hints for the compiler, while others are used at runtime.

有些注释是作为编译器的提示,而有些则是在运行时使用。

We use the @Retention annotation to say where in our program’s lifecycle our annotation applies.

我们使用@Retention注解来说明我们的注解在程序的生命周期中的适用位置

To do this, we need to configure @Retention with one of three retention policies:

要做到这一点,我们需要用三种保留策略之一来配置@Retention

  1. RetentionPolicy.SOURCE – visible by neither the compiler nor the runtime
  2. RetentionPolicy.CLASS – visible by the compiler
  3. RetentionPolicy.RUNTIME – visible by the compiler and the runtime

If no @Retention annotation is present on the annotation declaration, then the retention policy defaults to RetentionPolicy.CLASS.

如果注释声明中没有@Retention注释,那么保留政策默认为RetentionPolicy.CLASS.

If we have an annotation that should be accessible at runtime:

如果我们有一个注解,应该在运行时访问。

@Retention(RetentionPolicy.RUNTIME)
@Target(TYPE)
public @interface RetentionAnnotation {
}

Then, if we add some annotations to a class:

那么,如果我们给一个类添加一些注解。

@RetentionAnnotation
@Generated("Available only on source code")
public class AnnotatedClass {
}

Now we can reflect on AnnotatedClass to see how many annotations are retained:

现在我们可以对AnnotatedClass进行反思,看看有多少注释被保留下来。

@Test
public void whenAnnotationRetentionPolicyRuntime_shouldAccess() {
    AnnotatedClass anAnnotatedClass = new AnnotatedClass();
    Annotation[] annotations = anAnnotatedClass.getClass().getAnnotations();
    assertThat(annotations.length, is(1));
}

The value is 1 because @RetentionAnnotation has a retention policy of RUNTIME while @Generated doesn’t.

该值为1,因为@RetentionAnnotation的保留策略为RUNTIME,而@Generated则没有。

4.3. @Inherited

4.3.@Inherited

In some situations, we may need a subclass to have the annotations bound to a parent class.

在某些情况下,我们可能需要一个子类将注解绑定到父类。

We can use the @Inherited annotation to make our annotation propagate from an annotated class to its subclasses.

我们可以使用@Inherited 注解来使我们的注解从一个被注解的类传播到其子类。

If we apply @Inherited to our custom annotation and then apply it to BaseClass:

如果我们将@Inherited应用于我们的自定义注解,然后将其应用于BaseClass

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotation {
}

@InheritedAnnotation
public class BaseClass {
}

public class DerivedClass extends BaseClass {
}

Then, after extending the BaseClass, we should see that DerivedClass appears to have the same annotation at runtime:

然后,在扩展BaseClass之后,我们应该看到DerivedClass在运行时出现了相同的注解。

@Test
public void whenAnnotationInherited_thenShouldExist() {
    DerivedClass derivedClass = new DerivedClass();
    InheritedAnnotation annotation = derivedClass.getClass()
      .getAnnotation(InheritedAnnotation.class);
 
    assertThat(annotation, instanceOf(InheritedAnnotation.class));
}

Without the @Inherited annotation, the above test would fail.

如果没有@Inherited注解,上述测试将会失败。

4.4. @Documented

4.4.@Documented

By default, Java doesn’t document the usage of annotations in Javadocs.

默认情况下,Java不会在Javadocs中记录注解的用法。

But, we can use the @Documented annotation to change Java’s default behavior.

但是,我们可以使用@Documentedannotation来改变Java的默认行为

If we create a custom annotation that uses @Documented:

如果我们创建一个使用@Documented的自定义注解。

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelCell {
    int value();
}

And, apply it to the appropriate Java element:

并且,将其应用于适当的Java元素。

public class Employee {
    @ExcelCell(0)
    public String name;
}

Then, the Employee Javadoc will reveal the annotation usage:

然后,Employee Javadoc将揭示注释的用法。

Field Detail

4.5. @Repeatable

4.5 @可重复

Sometimes it can be useful to specify the same annotation more than once on a given Java element.

有时,在一个给定的Java元素上不止一次地指定相同的注解是很有用的。

Before Java 7, we had to group annotations together into a single container annotation:

在Java 7之前,我们必须把注解组合到一个容器注解中。

@Schedules({
    @Schedule(time = "15:05"),
    @Schedule(time = "23:00")
})
void scheduledAlarm() {
}

However, Java 7 brought a cleaner approach. With the @Repeatable annotation, we can make an annotation repeatable:

然而,Java 7带来了一种更简洁的方法。通过@Repeatable注解,我们可以使一个注解可重复使用

@Repeatable(Schedules.class)
public @interface Schedule {
    String time() default "09:00";
}

To use @Repeatable, we need to have a container annotation, too. In this case, we’ll reuse @Schedules:

为了使用@Repeatable,我们也需要有一个容器注解在这种情况下,我们将重新使用@Schedules

public @interface Schedules {
    Schedule[] value();
}

Of course, this looks a lot like what we had before Java 7. But, the value now is that the wrapper @Schedules isn’t specified anymore when we need to repeat @Schedule:

当然,这看起来很像我们在Java 7之前的情况。但是,现在的价值在于,当我们需要重复@Schedule时,包装器@Schedules不再被指定。

@Schedule
@Schedule(time = "15:05")
@Schedule(time = "23:00")
void scheduledAlarm() {
}

Because Java requires the wrapper annotation, it was easy for us to migrate from pre-Java 7 annotation lists to repeatable annotations.

因为Java需要包装器注解,所以我们很容易从Java 7之前的注解列表迁移到可重复注解。

5. Conclusion

5.结论

In this article, we’ve talked about Java built-in annotations that every Java developer should be familiar with.

在这篇文章中,我们已经谈到了每个Java开发者都应该熟悉的Java内置注解。

As always, all the examples of the article can be found over on GitHub.

一如既往,文章的所有例子都可以在GitHub上找到over