Java Annotation基础

基本参考自Java Annotation Tutorial doc:http://docs.oracle.com/javase/tutorial/java/annotations/index.html, 但因为不是全文翻译,也加了些自己的思考,所以选了原创


1. Annotation

Java Annotation是一种元数据(metadata),不是Java程序(逻辑)的一部分。Annotation有以下用途:

1.1 为编译器提供信息: 编译器可以根据annotation检查Error或者忽略Warning

1.2 编译和发布时信息处理: 一些工具可以利用Java code里的annotation信息生成必要的code、XML文件等等,比如Java doctools

1.3 运行时信息处理:一些annotation可以在运行是被读取,根据这些annotation,Java程序可以更加灵活的控制流程(比如TestNG里使用了大量运行时可检查的annotation)


2. Annotation基础

2.1 Annotation格式

Annotation用@符号标识,简单的比如:

@Override
void overrideFunction() { ... }

Annotation可以带元素(Elements),元素放在小括号里,有element name和element value,比如:

@Author (
name="navy", 
date="2014/10/13"
)
class TestClass { ... }
<pre name="code" class="java">@SupressWarnings(value="unchecked")
void deprecatedMethod { ... }

 

如果annotation只带一个元素,可以不用知道元素名,只提供元素的值即可

@SupressWarnings("unchecked")
void deprecatedMethod { ... }

可以同时用多个annotation:

@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
同一个annotation如果声明为repeating annotation,则可以同时在一个地方使用多次(没有声明的编译器会报错):

@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }

Annotation的类型可以是以下两个package中的任一类型: java.lang  or  java.lang.annotation,你也可以创建自己的annotation类型


2.2 什么地方可以使用Annotation

类、域、方法声明以及其他程序元素,当用在声明上的时候,通常annotation都写在单独的行内

在Java SE 8中,annotation还可以用于类型(Type)上,这种类型叫类型Annotation(Type Annotation),比如:

  • 创建类实例:
        new @Interned MyObject();
    
  • 类型上溯:
        myString = (@NonNull String) str;
    
  • implements 语句:
        class UnmodifiableList<T> implements
            @Readonly List<@Readonly T> { ... }
    
  • 违例声明:
        void monitorTemperature() throws
            @Critical TemperatureException { ... }

3. 声明(创建)一个Annotation类型

比如我们要为每一个类添加一个annotation type,记录这个类的作者等信息:

@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}

上例可以看到,Annotation类型的定义跟接口(Interface)有点类似,只不过在interface关键字前加了个@符号。实际上Annotation类型是接口的一种形式。

看看Annotation的内容,里面包含了annotation类型元素(Annotation Type Element)的声明,有点像方法,但是没有方法体。可以为这些元素提供一个默认值。定义好就可以使用这个Annotation type了:

@ClassPreamble (
   author = "John Doe",
   date = "3/17/2002",
   currentRevision = 6,
   lastModified = "4/12/2004",
   lastModifiedBy = "Jane Doe",
   // Note array notation
   reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {

// class code goes here

}


注意,如果要让这个annotation的内容被放到javadoc工具生成的文档里,就需要在annotation声明时使用@Documented annotation:

import java.lang.annotation.*
@Documented
@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}


4. 预定义的annotation类型

Java中预定义了一些annotation types,一些是编译器相关的,一些是用在别的Annotation类型上的(又叫meta-annotation)

4.1 用于编译器中的annotation类型(java.lang中定义):@Deprecated, @Override, @SuppressWarnings

@Deprecated 使用这个annotation的元素(class,method,field)都是不推荐再使用的,如果在code中使用编译时会有warning。而且当一个元素被抛弃(不再使用),除了使用@Deprecated annotation外,还要添加javadoc的@deprecated annotation(注意这两个不一样,首字母一个大写一个小写):

   // Javadoc comment follows
    /**
     * @deprecated
     * explanation of why it was deprecated
     */
    @Deprecated
    static void deprecatedMethod() { }
}


@Override 提示编译器这个元素(method)是覆盖了一个超类中的元素,如果使用了@Override但是实际没有覆盖任何元素的话,编译器会报错

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

@SuppressWarnings 告诉编译器忽略指定的warning,Java有两种类型的warning:deprecation和unchecked,下例忽略deprecation的warning

// use a deprecated method and tell 
   // compiler not to generate a warning
   @SuppressWarnings("deprecation")
    void useDeprecatedMethod() {
        // deprecation warning
        // - suppressed
        objectOne.deprecatedMethod();
    }

@SafeVarargs 在方法或构造器上使用时,表明方法内没有对Varargs执行潜在的不安全操作,使用这个annotation类型时,Varargs相关的unchecked warning会被忽略


@FunctionalInterface 表明声明应该是一个功能性的接口,这是Java SE 8新引入的


4.2 用在其他annotation类型上的annotation(meta-annotation): 在java.lang.annotation中定义

@Retention 指出annotation存在于哪个level

  • RetentionPolicy.SOURCE – 只存在于源码level,编译器看不见
  • RetentionPolicy.CLASS – 只有编译时被编译器使用,JVM看不见
  • RetentionPolicy.RUNTIME – 运行时环境里也可以看见,可以被JVM使用
@Documented 被这个annotation type标识了的annotation,里面所有的元素都会被javadoc工具用于生成文档

@Target 限制哪些Java元素可以使用这个annotation,它指定下列之一作为value

  • ElementType.ANNOTATION_TYPE 可用于annotation类型
  • ElementType.CONSTRUCTOR 用于构造器
  • ElementType.FIELD 用于域或者属性
  • ElementType.LOCAL_VARIABLE 用于本地变量
  • ElementType.METHOD 用于方法
  • ElementType.PACKAGE 用于包声明
  • ElementType.PARAMETER 用于方法的参数
  • ElementType.TYPE 用于类的任何元素
@Inherited 指出这个annotation类型可以从超类中继承(但是默认是不能继承的),使用了这个annotation的annotation type只能用于类声明中。当用户获取一个类的annotation类型(使用了@Inherited)失败时,会尝试从超类中获取。

@Repeatable Java SE 8中新加的,被标记的annotation类型可以在同一个元素上使用多次


5. 类型Annotation及便携式类型系统

Java SE 8之前,annotation只能应用在声明中(方法,类,域),但是现在annotation可以用于任何的使用类型的地方(Type Use),参见2.2中的实例。

类型annotation被用来强化Java程序分析中的类型检查,它让用户可以自己写一个(或使用一个别人的)类型检查框架,与编译器配合使用。

比如想保证一个变量绝对不会为null,你可以写一个plug-in来检查,在code中给那个变量添加一个annotation指明它不能为null:

@NonNull String str;
编译时,将NonNull模块包含到命令行中,编译器在检查到潜在的问题是会报warning,你可以修改代码防止错误发生。

你可以使用多个类型检查的模块,每个模块检查不同的错误,这样就可以在编译时添加特定的检查以避免相应错误,同时帮助你写出更健壮不易出错的代码。

现在有很多的第三方的类型检查模块可以使用,比如Checker Framework created by the University of Washington

更多内容可参考:http://types.cs.washington.edu/checker-framework/


6. 重复Annotation(Repeating Annotations)

有时候我们想在同一个声明或者类型上重复使用相同的Annotation类型,这时需要用到重复Annotation。

@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }
考虑到兼容性问题,重复annotations被存放在一个编译器自动生成的container annotation中,为此你需要以下两个声明:

6.1 声明一个重复的annotation类型: 必须标识为@Repeatable,括号中指明了这个annotation类型的container annotation,Schedule的container annotation就是Schedules,所以@Schedule annotations就被存放在一个@Schedules annotation中

import java.lang.annotation.Repeatable;
@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

6.2 声明container annotation类型

container annotation类型必须有一个array类型的,名为value的元素,且这个array类型的component type必须是要用到这个container annotation的repeatable annotation类型:

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

7. 获取annotation

反射(Reflection)API里有几种方法可以获取annotation相关信息,比如AnnotatedElement.getAnnotationByType(Class<T>),  AnnotatedElement.getAnnotations(Class<T>)

跟多获取annotation的方法见:http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AnnotatedElement.html





                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值