或许你经常使用框架,经常使用注解进行快捷开发,但是你了解注解是怎么实现的吗,本篇博客将探讨下java中的四个元注解
首先说下jdk1.8在注解方面的新特性:jdk1,8新增了重复注解与类型注解
重复注解:
在1.8以前,同一个程序元素前最多只能有一个相同类型的注解; 如果需要在同一个元素前使用多个相同类型的注解,必须要使用注解容器。
public@interfaceAuthority {
String role();
}
public@interfaceAuthorities { //@Authorities注解作为可以存储多个@Authority注解的容器
Authority[] value();
}
publicclassRepeatAnnotationUseOldVersion {
@Authorities({@Authority(role="Admin"),@Authority(role="Manager")})
publicvoiddoSomeThing(){
}
}
jdk1.8新增了重复注解,其使用方式为:
@Repeatable(Authorities.class)
public@interfaceAuthority {
String role();
}
public@interfaceAuthorities {
Authority[] value();
}
publicclassRepeatAnnotationUseNewVersion {
@Authority(role="Admin")
@Authority(role="Manager")
publicvoiddoSomeThing(){ }
}
不同的地方是,创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities,在使用时候,直接可以重复使用Authority注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点。但是,仍然需要定义容器注解。
类型注解:
Java8为ElementType枚举增加了TYPE_PARAMETER、TYPE_USE两个枚举值,从而可以使用@Target(TYPE_PARAMETER),@Target(TYPE_PARAMETER)修饰注解定义,这种注解被称为类型注解,可以用在任何使用到类型的地方。具体在下面说。
java中有四个元注解:
1. @Retention 2. @Target 3. @Document 4. @Inherited
如何建一个注解类? 新建文件时选择Annotation文件即可
看下四个注解的源码:
1. @Retention注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
可以看到这个注解类中定义了一个RetentionPolicy方法,说明在使用retention注解时,可以在注解后面的括号里填入一个retentionPolicty实例,我们再看下retentionPolicity的源码:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
可以看到这是一个枚举类,里面有三个属性,这每一个属性都代表一个枚举实例,这三个属性定义了注解的存在时间
@Retention(RetentionPolicy.SOURCE) 注解仅存在于源码中,在class类文件中不存在
@Retention(RetentionPolicy.CLASS) 默认的保留策略,注解在字节码文件中存在,但是运行时无法通过反射获得
@Retention(RetentionPolicy.RUNTIME) 注解在class文件中存在,在运行时可以通过反射获得
2.@Target注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
可以看到Target注解里面有一个ElementType数组,也就是说Target注解后可以定义多个ElementType对象,这个对象决定了@Target注解可以定义再哪个位置,再去看下ElementType的源码:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
一 一解释下里面属性的含义:
@Target(ElementType.TYPE) 注解可以定义在接口、类、枚举、注解上
@Target(ElementType.FIELD) 注解可以定义在字段、枚举的常量
@Target(ElementType.METHOD) 注解可以定义在方法上
@Target(ElementType.PARAMETER) 注解可以定义在方法参数上
@Target(ElementType.CONSTRUCTOR) 注解可以定义在构造方法上
@Target(ElementType.LOCAL_VARIABLE) 注解可以定义在局部变量上
@Target(ElementType.ANNOTATION_TYPE) 注解可以定义在注解上
@Target(ElementType.PACKAGE) 注解可以声明在包上
还有两个JDK1.8新增的类型:
@Target(ElementType.TYPE.PARAMETER) 用于类型参数声明语句,这里可能大家不明白类型参数是什么,所以举个例子:
@Target(ElementType.TYPE_PARAMETER)
public @interface NotNull {
}
class MyBox <@NotNull T, @NotNull S> {
private T t;
private S s;
public void add(T t, S s) {
this.t = t;
this.s = s;
}
public T getFirst() {
return t;
}
public S getSecond() {
return s;
}
}
@Target(ElementType.TYPE_USE) 表示注解可以在任何用到类型的地方使用,比如以下位置:
1. 创建对象(用new关键字创建)
2. 类型转换
3. 使用implements实现接口
4.使用throws声明抛出异常
@Target(ElementType.TYPE_USE)
@interface NotNull{ }
//定义类时使用
@NotNull
public class TypeAnnotationTest implements Serializable //在implements时使用
{
//在方法形参中使用
public static void main(@NotNull String [] args) throws @NotNull FileNotFoundException //在throws时使用
{
Object obj="fkjava.org";
//使用强制类型转换时使用
String str=(@NotNull String) obj;
//创建对象时使用
Object win=new (@NotNull) JFrame("疯狂软件");
}
//泛型中使用
public void foo(List<@NotNull String> info) { }
}
3.@Document注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
拥有这个注解的元素可以被javadoc此类的工具文档化。它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@return,@param 等。
4.@Inherited注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
定义注解类A,A中使用@Inherited,在B中使用注解A,C继承B,C可以继承B中使用的注解A
最后送大家一段苹果的广告语,致疯狂的人
他们特立独行。他们桀骜不驯。他们惹是生非。他们格格不入。他们用与众不同的眼光看待事物。他们不喜欢墨守成规。他们也不愿安于现状。你可以认同他们,反对他们,颂扬或是诋毁他们。但唯独不能漠视他们。因为他们改变了寻常事物。他们推动人类向前迈进。或许他们是别人眼里的疯子,但他们却是我们眼中的天才。因为只有那些疯狂到以为自己能够改变世界的人,才能真正改变世界。