关于程序员:讲讲Java的注解

3次阅读

共计 6253 个字符,预计需要花费 16 分钟才能阅读完成。

Java SE 5 引入了许多的语言变动,注解(元数据)就是其中之一,次要是在代码中附加补充信息。注解不会影响代码的逻辑,只是在编译、运行时,为代码退出配置与形容等性能。

注解应用 @interface 来定义注解。如下所示:

@interface Mapping {String name();
    String[] path() default {};
    Type type() default Type.GET;}

注解不能蕴含 extends 子句。然而,所有的注解类型都隐式地间接扩大自 java.lang.annotation.Annotation 接口。

Mapping 注解中只须要一个注解元素时,能够将名称设成 value,这样指定值时就不须要指定注解元素的名称了。如 @Mapping("get")

注解中的元素类型能够应用根本类型、StringClassenum、注解类型以及后面所述类型组成的数组。而且能够应用 default 为注解元素提供默认值。如上所示的 String[] path() default {} 就是设置了默认值的注解元素。

注解申明

定义完的注解须要在代码中应用,申明注解能够呈现在包、类、接口、办法、结构器、实例域、局部变量、参数变量以及参数类型上。上面给出一个例子:

@Mapping("example") public class Sample {...}

留神:对局部变量的注解只能在源码级别上进行解决。类文件并不形容局部变量。因而,所有的局部变量注解在编译完一个类的时候就会被遗弃掉。同样地,对包的注解不能在源码级别之外存在。

内置注解

Java SE 提供了许多注解,但其中大部分是专用注解。其中有规定注解和元注解。

  • @Deprecated 该注解用于标记不想被应用的项上。当应用 @Deprecated 标记时,编译器就会收回正告。这个注解与 Javadoc 标签 @deprecated 具备等同效用。
  • @SuppressWarnings 该注解会告知编译器阻止特定类型的正告信息。如 @SuppressWarnings("unchecked")
  • @Override 该注解只能利用于办法上,示意以后的办法定义将笼罩基类的办法。如果你的类中重写 equals(Object obj) 办法时,就能够应用 @Override 注解标记。不是被笼罩的办法就不要用 @Override,那样编译器会报错。
  • @SafeVarargs 该注解用于对具备泛型 varargs 参数可平安应用。作用于办法或结构器上。该注解为 Java SE 7 新增注解。
  • @FunctionalInterface 该注解用于标记只有一个形象办法的接口,申明为函数式接口。该注解为 Java SE 8 新增注解。

其中,不蕴含注解元素的非凡类型的注解被称为标记注解,它惟一的目标就是标记申明。确定标记注解是否存在的最好形式是应用 isAnnotationPresent() 办法,该办法是由 AnnotatedElement 接口定义的。

元注解

还有一些注解用以形容注解接口的行为属性,这种注解称为元注解(meta annotation)。Java 以提供了一些元注解。如下所示:

@Retention

@Retention 定义注解的保留策略,用以决定注解在什么地位被抛弃。而保留策略被封装在 java.lang.annotation.RetentionPolicy 枚举中。如下所示:

  • RetentionPolicy.SOURCE 只在源文件中保留,在编译期间会被摈弃。
  • RetentionPolicy.CLASS 在编译期间被存储到 .class 文件中。然而会在运行时通过 JVM 不能失去这些注解。
  • RetentionPolicy.RUNTIME 在编译期间被存储到 .class 文件中,并且在运行时能够通过 JVM 获取这些注解。因而,能够通过反射机制读取注解的信息。

留神:局部变量申明的注解不能存储在 .class 文件中。

如果 @Retention 不存在,则该 Annotation 默认为 CLASS。因为通常咱们自定义的Annotation 都是 RUNTIME,所以,务必要加上@Retention(RetentionPolicy.RUNTIME) 这个元注解:

@Retention(RetentionPolicy.RUNTIME)
public @interface Mapping {String name();
    String[] path() default {};
    Type type() default Type.GET;}

在程序执行期间通过 JVM 能够获取 Mapping 注解。

@Target

@Target 定义注解的申明类型,用来决定注解利用的地位,如类、办法、变量、参数以及包等。而定义的类型封装在 java.lang.annotation.ElementType 枚举中。如下所示:

  • ElementType.ANNOTATION_TYPE 注解类型申明。
  • ElementType.CONSTRUCTOR 结构器申明。
  • ElementTyoe.FIELD 域变量申明。
  • ElementType.LOCAL_VARIABLE 局部变量申明。
  • ElementType.METHOD 办法申明。
  • ElementType.PACKAGE 包申明。
  • ElementType.PARAMETER 参数申明。
  • ElementType.TYPE 类、接口或者枚举申明。
  • ElementType.TYPE_PARAMETER 类型参数。
  • ElementType.TYPE_USE 类型应用。

如下所示,定义的注解 @Mapping 可用在办法上:

@Target(ElementType.METHOD)
public @interface Mapping {String name();
    String[] path() default {};
    Type type() default Type.GET;}

@Target 注解中也能够指定多个值,并用 {} 包围起来。如下所示:

@Target({
    ElementType.METHOD, 
    ElementType.FIELD})
public @interface Mapping {String name();
    String[] path() default {};
    Type type() default Type.GET;}

如果不应用 @Target,能够利用于任何申明上。因而,一般来说,显式指定指标是个好主见,能够明确阐明注解的用处。

TYPE_PARAMETERTYPE_USE 枚举常量都是 Java SE 8 新减少的枚举常量。类型注解必须蕴含 ElementType_USE,这样就能够注解办法的返回类型、办法内 this 的类型、强制转换、数组级别、被继承的类以及 throws 子句。还能够注解泛型,包含泛型类型参数边界和泛型类型参数。

@Documented

@Documented 注解是一个标记接口,用于告知某个工具注解将被文档化。@Documented 被设计为只能注解其余注解。

默认状况下,javadoc 不解决注解,但在注解上标记 @Documented,就会被像 javadoc 这样的归档工具解决,以实现归档。

@Documented
@Target(ElementType.METHOD)
public @interface Mapping {String name();
    String[] path() default {};
    Type type() default Type.GET;}

@Inherited

@Inherited 是一个标记注解,用于另外一个注解申明。@Inherited 标记的注解能够被子类继承,而且只能用于被 @Target(ElementType.TYPE) 类型标注的注解。

因而,当查问子类的特定注解时,如果那种注解在子类中不存在,就会查看基类。如果那种注解存在于超类中,并且如果应用 @Inherited 进行了注解,就将返回那种注解。

定义一个标记了 @Inherited 的注解 @Persistent

@Inherited @interface Persistent {}

这里应用 @Persistent 注解来指明一个类的对象能够存储到数据库中。

@Persistent public class Person {}

那么继承自 Person 的子类也会主动被注解为长久化。

public class Student extends Person {}

@Repeatable

Java SE 8 能够应用 @Repeatable 注解将同种类型的注解屡次利用于某一项。为放弃向后兼容,可反复注解的实现者须要提供一个容器注解,它能够将这些反复注解存储到一个数组中。

@Repeatable(Mappings.class)
@Target(ElementType.METHOD)
public @interface Mapping {String name();
    String path() default“”;
    Type type() default Type.GET;}
@Target(ElementType.Type)
public @interface Mappings {Mapping[] value();}

通过 @Repeatable 润饰后,只有用户提供两个或更多 @Mapping 注解,那就会主动地被包装到 @Mappings 注解中。

@Mapping(name="GET Mapping", path="/profile")
@Mapping(name="POST Mapping", path="/profile", type=Type.POST)
public class AnnotationTest {}

这里,应用 getAnnotation() 办法获取反复注解的话,须要传入的是容器注解的类,而不是反复注解自身。AnnotatedElement 接口还提供了 getAnnotationsByType()getDeclaredAnnotationsByType() 这两个办法来间接操作反复注解。

@Native

应用 @Native 注解润饰成员变量,示意这个变量能够被本地代码援用,经常被代码生成工具应用。@Native 注解不常应用。

注解解决

当注解指定 RUNTIME 保留策略时,运行时就能够通过反射 API 来查问注解。反射 API 用到的类如 ClassFieldMethod 以及 Constructor 都实现了 AnnotatedElement 注解接口。

该接口提供了几个办法来获取注解。其中 getAnnotationsByType()getDeclaredAnnotation()以及 getDelaredAnnotationsByType() 办法都是 Java SE 8 新增办法。

getAnnotation()

AnnotatedElement 注解接口提供的 getAnnotation() 办法能够取得与对象关联的注解。该办法的个别模式如下:

<T extends Annotation> T getAnnotation(Class<T> annotationClass)

这里的 annotationClass 示意你要查找的注解类型。该办法返回对注解的一个援用,应用这个援用能够获取与注解元素关联的值。如果没有找到注解,该办法会返回 null。上面应用该办法获取 @Mapping 注解。

Class<?> cls = AnnotationTest.class;
cls.getAnnotation(Mapping.class);    // 获取 Mapping 注解

getAnnotations()

AnnotatedElement 注解接口提供的 getAnnotations() 办法会返回 ClassMethodConstructor 以及 Field 等类型的对象中的所有注解的一个注解数组。该办法的个别模式如下:

Annotation[] getAnnotations()

上面应用该办法获取所有注解。

Class<?> cls = AnnotationTest.class;
cls.getAnnotations();    // 获取 cls 对象中的所有注解

getDeclaredAnnotations()

AnnotatedElement 注解接口提供的 getDeclaredAnnotations() 办法返回的是调用对象中存在的所有非继承注解。该办法的个别模式如下:

Annotation[] getDeclaredAnnotations()

isAnnotationPresent()

AnnotatedElement 注解接口提供的 isAnnotationPresent() 办法用于判断调用对象是否存在指定的注解。该办法的个别模式如下:

default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)`

如果 annotationClass 指定的注解与调用对象相关联,返回 true,否则返回 false。上面应用该办法判断 @Mapping 注解是否存在。

Class<?> cls = AnnotationTest.class;
if (cls.isAnnotationPresent(Mapping.class)) {...}

getDeclaredAnnotation()

AnnotatedElement 注解接口提供的 getDeclaredAnnotation() 办法返回与对象关联的注解。该办法会疏忽继承注解,个别模式如下:

default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {...}

这里的 annotationClassgetAnnotation() 的参数一样,都是示意要查找的注解类型。

getAnnotationsByType()

AnnotatedElement 注解接口提供的 getAnnotationsByType() 办法用以获取与对象关联的注解。如果没有与此元素关联的注解,则返回值是长度为 0 的数组。该办法的个别模式如下:

default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {...}

该办法与 getAnnotation(Class<T> annotationClass) 的区别在于,检测其注解对应的反复注解容器。

getDeclaredAnnotationsByType()

AnnotatedElement 注解接口提供了 getDeclaredAnnotationsByType() 办法。如果注解间接存在或间接存在,则返回该元素的注解,如果没有,则返回值为长度为 0 的数组。该办法的个别模式如下:

default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {...}

此办法疏忽继承的注解,而且与 getDeclaredAnnotation(Class<T> annotationClass) 的区别在于,检测其参数是否为可反复的注解类型,如果是,尝试查看反复注解容器来查找该类型的一个或多个注解。

总结

通过注解,能够在不影响代码语义的状况下,提供了其形容信息,这些信息可能被内部工具主动解决。能够通过反射 API 获取注解,并进行操作。文档生成、编译查看、动静解决等方面都能够应用注解。

更多内容请关注公众号「海人为记」

正文完
 0