创建注释是一个相当简单的过程,尽管它受到某些规则的限制。现在我们需要弄清楚它们的实际用途是什么。

让我们回想一下我们是如何创建自己的注解的。

我们将为类和方法编写一个注释,并包含有关代码作者和版本的信息:


@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Info {
   String author() default "Author";
   String version() default "0.0";
}

这是我们注释的类:


@Info
public class MyClass1 {
   @Info
   public void myClassMethod() {}
}
 
@Info(version = "2.0")
public class MyClass2 {
   @Info(author = "Anonymous")
   public void myClassMethod() {}
}
 
@Info(author = "Anonymous", version = "2.0")
public class MyClass3 {
   @Info(author = "Anonymous", version = "4.0")
   public void myClassMethod() {}
}

我们如何在运行时使用这些数据?

通过使用反射从注释中提取元数据。回想一下反射是什么。反射是一种在运行时检查程序数据的机制。反射让您获得有关字段、方法、类构造函数和类的信息。

我们将使用反射来读取类中的注释并显示我们想要的信息。

我们将在main方法中从我们的类中读取数据:


import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
 
public class Main {
   public static void main(String[] args) throws NoSuchMethodException {
       readMyClass(MyClass1.class);
       readMyClass(MyClass2.class);
       readMyClass(MyClass3.class);
   }
 
   static void readMyClass(Class<?> myClassObj) throws NoSuchMethodException {
       System.out.println("\nClass: " + myClassObj.getName());
       readAnnotation(myClassObj);
       Method method = myClassObj.getMethod("myClassMethod");
       readAnnotation(method);
   }
 
   static void readAnnotation(AnnotatedElement element) {
       try {
           System.out.println("Search for annotations in " + element.getClass().getName());
           Annotation[] annotations = element.getAnnotations();
           for (Annotation annotation : annotations) {
               if (annotation instanceof Info) {
                   final Info fileInfo = (Info) annotation;
                   System.out.println("Author: " + fileInfo.author());
                   System.out.println("Version: " + fileInfo.version());
               }
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

我们将我们类的一个实例传递给readMyClass方法。

然后我们可以将一个类和一个方法传递给readAnnotation方法。让我们这样做——我们将传递一个 Class 对象和一个 Method 对象。它需要一个实现AnnotatedElement接口的对象。这使我们可以从对象中获取注释列表并获取有关每个注释的信息。

请注意,我们只有在检查注解是否属于我们的注解类型后才能获取信息:if (annotation instanceof Info)

程序输出为我们提供了来自注释的完整信息:

Class: annotation.MyClass1
在java.lang.Class中搜索注解
作者: Author
Version: 0.0
在java.lang.reflect.Method中搜索注解
作者: Author
Version: 0.0

Class: annotation.MyClass2
在java.lang中搜索注解。类
作者:Author
版本:2.0
java.lang.reflect.Method 中搜索注解
作者:Anonymous
版本:0.0

类:annotation.MyClass3
java.lang.Class 中搜索注解
作者:Anonymous
版本:2.0
java 中搜索注解。 lang.reflect.Method
作者:匿名
版本:4.0

因此,在反射的帮助下,我们能够提取元数据。

现在让我们看一个使用注释来改进代码的示例,包括总体上提高可读性、速度和质量。龙目岛将帮助我们解决这个问题。

Lombok 是一个编译器插件,它使用注释来隐藏大量代码并扩展语言,从而简化开发并添加一些功能。

考虑来自 Lombok 的注释示例:

@ToString 生成toString()方法的实现,该方法由对象的完整表示组成:类名、所有字段及其值。

@ToString
public class Example
@EqualsAndHashCode 生成默认使用非静态和非静态字段但可配置的equalshashCode的实现。 可以在该项目的网站上找到更多详细信息。在那里您会找到一个使用@EqualsAndHashCode的示例,并且还显示了一个没有注释的标准实现。
@Getter / @Setter 为私有字段生成 getter 和 setter。

@Getter 
@Setter 
private String name = "name";
@非空 用于在实例化对象时断言字段不为空。否则,将抛出NullPointerException 。

public Example(@NonNull P p) {
 super("Hello");
 this.name = p.getName();
}

Lombok 有许多不太常用的有用的注解。我们已经考虑了它最简单的注解。您可以在官方网站上阅读有关该项目的更多信息。