原文: https://www.programiz.com/java-programming/annotation-types

在本教程中,我们将借助示例学习不同类型的 Java 注解。

Java 注解是程序源代码的元数据(有关数据的数据)。 Java SE 提供了几个预定义的注解。 此外,我们还可以根据需要创建自定义注解。

如果您不知道什么是注解,请访问 Java 注解教程。

这些注解可以分类为:

  1. 预定义的注解
  • @Deprecated
  • @Override
  • @SuppressWarnings
  • @SafeVarargs
  • @FunctionalInterface
  1. 自定义注解

  2. 元注解

  • @Retention
  • @Documented
  • @Target
  • @Inherited
  • @Repeatable

预定义的注解类型

1. @Deprecated

@Deprecated注解是标记注解,指示不赞成使用元素(类,方法,字段等),并已用较新的元素替换。

其语法为:

  1. @Deprecated
  2. accessModifier returnType deprecatedMethodName() { ... }

当程序使用已声明为已弃用的元素时,编译器将生成警告。

我们使用 Javadoc @deprecated标记来记录已弃用的元素。

  1. /**
  2. * @deprecated
  3. * why it was deprecated
  4. */
  5. @Deprecated
  6. accessModifier returnType deprecatedMethodName() { ... }

示例 1:@Deprecated注解示例

  1. class Main {
  2. /**
  3. * @deprecated
  4. * This method is deprecated and has been replaced by newMethod()
  5. */
  6. @Deprecated
  7. public static void deprecatedMethod() {
  8. System.out.println("Deprecated method");
  9. }
  10. public static void main(String args[]) {
  11. deprecatedMethod();
  12. }
  13. }

输出

  1. Deprecated method

2. @Override

@Override注解指定子类的方法使用相同的方法名称,返回类型和参数列表覆盖超类的方法。

覆盖方法时,并非必须使用@Override。 但是,如果使用它,则在覆盖该方法时,如果出现错误(例如错误的参数类型),则编译器将给出错误。

示例 2:@Override注解示例

  1. class Animal {
  2. // overridden method
  3. public void display(){
  4. System.out.println("I am an animal");
  5. }
  6. }
  7. class Dog extends Animal {
  8. // overriding method
  9. @Override
  10. public void display(){
  11. System.out.println("I am a dog");
  12. }
  13. public void printMessage(){
  14. display();
  15. }
  16. }
  17. class Main {
  18. public static void main(String[] args) {
  19. Dog dog1 = new Dog();
  20. dog1.printMessage();
  21. }
  22. }

输出

  1. I am a dog

在此示例中,通过使Dog类的对象dog1,我们可以调用其方法printMessage(),该方法然后执行display()语句。

由于在两个类中都定义了display(),因此Dog子类的方法将覆盖Animal超类的方法。 因此,将调用子类的display()


3. @SuppressWarnings

顾名思义,@SuppressWarnings注解指示编译器禁止在程序执行时生成警告。

我们可以指定要取消的警告类型。 可以禁止的警告是特定于编译器的,但有两类警告:弃用未选中

为了禁止显示特定类别的警告,我们使用:

  1. @SuppressWarnings("warningCategory")

例如,

  1. @SuppressWarnings("deprecated")

为了禁止显示多类警告,我们使用:

  1. @SuppressWarnings({"warningCategory1", "warningCategory2"})

例如:

  1. @SuppressWarnings({"deprecated", "unchecked"})

类别deprecated指示编译器在使用不推荐使用的元素时禁止显示警告。

类别unchecked指示编译器在使用原始类型时禁止显示警告。

并且,未定义的警告将被忽略。 例如,

  1. @SuppressWarnings("someundefinedwarning")

示例 3:@SuppressWarnings注解示例

  1. class Main {
  2. @Deprecated
  3. public static void deprecatedMethod() {
  4. System.out.println("Deprecated method");
  5. }
  6. @SuppressWarnings("deprecated")
  7. public static void main(String args[]) {
  8. Main depObj = new Main();
  9. depObj. deprecatedMethod();
  10. }
  11. }

输出

  1. Deprecated method

在这里,deprecatedMethod()已被标记为已弃用,并且在使用时会发出编译器警告。 通过使用@SuppressWarnings("deprecated")注解,我们可以避免编译器警告。


4. @SafeVarargs

@SafeVarargs注解断言,带注解的方法或构造器不会对其可变参数(可变数量的参数)执行不安全的操作。

我们只能在不能被覆盖的方法或构造器上使用此注解。 这是因为覆盖它们的方法可能执行不安全的操作。

在 Java 9 之前,我们只能在finalstatic方法上使用此注解,因为它们不能被覆盖。 现在,我们也可以将此注解用于私有方法。

示例 4:@SafeVarargs注解示例

  1. import java.util.*;
  2. class Main {
  3. private void displayList(List<String>... lists) {
  4. for (List<String> list : lists) {
  5. System.out.println(list);
  6. }
  7. }
  8. public static void main(String args[]) {
  9. Main obj = new Main();
  10. List<String> universityList = Arrays.asList("Tribhuvan University", "Kathmandu University");
  11. obj.displayList(universityList);
  12. List<String> programmingLanguages = Arrays.asList("Java", "C");
  13. obj.displayList(universityList, programmingLanguages);
  14. }
  15. }

警告

  1. Type safety: Potential heap pollution via varargs parameter lists
  2. Type safety: A generic array of List<String> is created for a varargs
  3. parameter

输出

  1. Note: Main.java uses unchecked or unsafe operations.
  2. [Tribhuvan University, Kathmandu University]
  3. [Tribhuvan University, Kathmandu University]
  4. [Java, C]

在这里,List ... lists指定类型为List的变长参数。 这意味着方法displayList()可以具有零个或多个参数。

上面的程序编译没有错误,但是当不使用@SafeVarargs注解时会发出警告。

在上面的示例中使用@SafeVarargs注解时,

  1. @SafeVarargs
  2. private void displayList(List<String>... lists) { ... }

我们得到相同的输出,但没有任何警告。 当使用此注解时,非受检的警告也会被删除。


5. @FunctionalInterface

Java 8 首先引入了此@FunctionalInterface注解。 该注解指示使用它的类型声明是一个函数式接口。 一个函数式接口只能有一个抽象方法。

示例 5:@FunctionalInterface注解示例

  1. @FunctionalInterface
  2. public interface MyFuncInterface{
  3. public void firstMethod(); // this is an abstract method
  4. }

如果我们添加另一个抽象方法,那么说

  1. @FunctionalInterface
  2. public interface MyFuncInterface{
  3. public void firstMethod(); // this is an abstract method
  4. public void secondMethod(); // this throws compile error
  5. }

现在,当我们运行程序时,我们将收到以下警告:

  1. Unexpected @FunctionalInterface annotation
  2. @FunctionalInterface ^ MyFuncInterface is not a functional interface
  3. multiple non-overriding abstract methods found in interface MyFuncInterface

使用@FunctionalInterface注解不是强制性的。 编译器会将满足函数式接口定义的任何接口视为函数式接口。

我们使用此注解来确保函数式接口只有一种抽象方法。

但是,由于它们具有实现,因此可以具有任意数量的默认方法和静态方法。

  1. @FunctionalInterface
  2. public interface MyFuncInterface{
  3. public void firstMethod(); // this is an abstract method
  4. default void secondMethod() { ... }
  5. default void thirdMethod() { ... }
  6. }

自定义注解

也可以创建我们自己的自定义注解。

它的语法是:

  1. [Access Specifier] @interface<AnnotationName> {
  2. DataType <Method Name>() [default value];
  3. }

这是您需要了解的有关自定义注解的信息:

  • 可以通过使用@interface和注解名称来创建注解。
  • 注解可以具有看起来像方法的元素,但是它们没有实现。
  • 默认值是可选的。 参数不能为空值。
  • 方法的返回类型可以是原始,枚举,字符串,类名或这些类型的数组。

示例 6:自定义注解示例

  1. @interface MyCustomAnnotation {
  2. String value() default "default value";
  3. }
  4. class Main {
  5. @MyCustomAnnotation(value = "programiz")
  6. public void method1() {
  7. System.out.println("Test method 1");
  8. }
  9. public static void main(String[] args) throws Exception {
  10. Main obj = new Main();
  11. obj.method1();
  12. }
  13. }

输出

  1. Test method 1

元注解

元注解是应用于其他注解的注解。

1. @Retention

@Retention注解指定注解可以使用的级别。

它的语法是:

  1. @Retention(RetentionPolicy)

保留策略有 3 种类型:

  • RetentionPolicy.SOURCE - 注解仅在源级别可用,并且被编译器忽略。
  • RetentionPolicy.CLASS - 注解在编译时可供编译器使用,但被 Java 虚拟机(JVM)忽略。
  • RetentionPolicy.RUNTIME - 注解可用于 JVM。

例如:

  1. @Retention(RetentionPolicy.RUNTIME)
  2. public @interface MyCustomAnnotation{ ... }

2. @Documented

默认情况下,自定义注解不包含在官方 Java 文档中。 为了将注解包含在 Javadoc 文档中,我们使用@Documented注解。

例如:

  1. @Documented
  2. public @interface MyCustomAnnotation{ ... }

3. @Target

我们可以使用@Target注解将注解限制为应用于特定目标。

它的语法是:

  1. @Target(ElementType)

ElementType可以具有以下类型之一:

元素类型 目标
ElementType.ANNOTATION_TYPE 注解类型
ElementType.CONSTRUCTOR 构造器
ElementType.FIELD 字段
ElementType.LOCAL_VARIABLE 局部变量
ElementType.METHOD 方法
ElementType.PACKAGE
ElementType.PARAMETER 参数
ElementType.TYPE 任何类元素

例如:

  1. @Target(ElementType.METHOD)
  2. public @interface MyCustomAnnotation{ ... }

在此示例中,我们仅将此注解的使用限制为方法。

注意:如果未定义目标类型,则注解可用于任何元素。


4. @Inherited

默认情况下,注解类型不能从超类继承。 但是,如果需要将注解从超类继承到子类,则可以使用@Inherited注解。

它的语法是:

  1. @Inherited

例如:

  1. @Inherited
  2. public @interface MyCustomAnnotation { ... }
  3. @MyCustomAnnotation
  4. public class ParentClass{ ... }
  5. public class ChildClass extends ParentClass { ... }

5. @Repeatable

@Repeatable标记的注解可以多次应用于同一声明。

  1. @Repeatable(Universities.class)
  2. public @interface University {
  3. String name();
  4. }

@Repeatable注解中定义的值是容器注解。 容器注解具有上述可重复注解的数组类型的变量value。 在此,Universities是包含注解的类型。

  1. public @interface Universities {
  2. University[] value();
  3. }

现在,@University注解可以在同一声明中多次使用。

  1. @University(name = "TU")
  2. @University(name = "KU")
  3. private String uniName;

如果需要检索注解数据,则可以使用 Reflection API

要检索注解值,我们使用在反射 API 中定义的getAnnotationsByType()getAnnotations()方法。