深入理解Java注解类型(@Annotation)
java注解是在jdk5时引入的新特性,鉴于目前大部分框架(如spring)都使用了注解简化代码并提高编码的效率,因此掌握并深入理解注解对于一个java工程师是来说是很有必要的事。本篇我们将通过以下几个角度来分析注解的相关知识点
理解java注解
实际上java注解与普通修饰符(public、static、void等)的使用方式并没有多大区别,下面的例子是常见的注解:
public class annotationdemo { //@test注解修饰方法a @test public static void a(){ system.out.println("test....."); } //一个方法上可以拥有多个不同的注解 @deprecated @suppresswarnings("uncheck") public static void b(){ } }
通过在方法上使用@test注解后,在运行该方法时,测试框架会自动识别该方法并单独调用,@test实际上是一种标记注解,起标记作用,运行时告诉测试框架该方法为测试方法。而对于@deprecated和@suppresswarnings(“uncheck”),则是java本身内置的注解,在代码中,可以经常看见它们,但这并不是一件好事,毕竟当方法或是类上面有@deprecated注解时,说明该方法或是类都已经过期不建议再用,@suppresswarnings 则表示忽略指定警告,比如@suppresswarnings(“uncheck”),这就是注解的最简单的使用方式,那么下面我们就来看看注解定义的基本语法
基本语法
声明注解与元注解
我们先来看看前面的test注解是如何声明的:
//声明test注解 @target(elementtype.method) @retention(retentionpolicy.runtime) public @interface test { }
我们使用了@interface声明了test注解,并使用@target注解传入elementtype.method参数来标明@test只能用于方法上,@retention(retentionpolicy.runtime)则用来表示该注解生存期是运行时,从代码上看注解的定义很像接口的定义,确实如此,毕竟在编译后也会生成test.class文件。对于@target和@retention是由java提供的元注解,所谓元注解就是标记其他注解的注解,下面分别介绍
@target 用来约束注解可以应用的地方(如方法、类或字段),其中elementtype是枚举类型,其定义如下,也代表可能的取值范围
public enum elementtype { /**标明该注解可以用于类、接口(包括注解类型)或enum声明*/ type, /** 标明该注解可以用于字段(域)声明,包括enum实例 */ field, /** 标明该注解可以用于方法声明 */ method, /** 标明该注解可以用于参数声明 */ parameter, /** 标明注解可以用于构造函数声明 */ constructor, /** 标明注解可以用于局部变量声明 */ local_variable, /** 标明注解可以用于注解声明(应用于另一个注解上)*/ annotation_type, /** 标明注解可以用于包声明 */ package, /** * 标明注解可以用于类型参数声明(1.8新加入) * @since 1.8 */ type_parameter, /** * 类型使用声明(1.8新加入) * @since 1.8 */ type_use }
请注意,当注解未指定target值时,则此注解可以用于任何元素之上,多个值使用{}包含并用逗号隔开,如下:
@target(value={constructor, field, local_variable, method, package, parameter, type})
@retention用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),其含有如下:
- source:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)
- class:注解在class文件中可用,但会被vm丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义retention值时,默认值是class,如java内置注解,@override、@deprecated、@suppresswarnning等
- runtime:注解信息将在运行期(jvm)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如springmvc中的@controller、@autowired、@requestmapping等。
注解元素及其数据类型
通过上述对@test注解的定义,我们了解了注解定义的过程,由于@test内部没有定义其他元素,所以@test也称为标记注解(marker annotation),但在自定义注解中,一般都会包含一些元素以表示某些值,方便处理器使用,这点在下面的例子将会看到:
/** * created by wuzejian on 2017/5/18. * 对应数据表注解 */ @target(elementtype.type)//只能应用于类上 @retention(retentionpolicy.runtime)//保存到运行时 public @interface dbtable { string name() default ""; }
上述定义一个名为dbtable的注解,该用于主要用于数据库表与bean类的映射(稍后会有完整案例分析),与前面test注解不同的是,我们声明一个string类型的name元素,其默认值为空字符,但是必须注意到对应任何元素的声明应采用方法的声明方式,同时可选择使用default提供默认值,@dbtable使用方式如下:
//在类上使用该注解 @dbtable(name = "member") public class member { //....... }
关于注解支持的元素数据类型除了上述的string,还支持如下数据类型
- 所有基本类型(int,float,boolean,byte,double,char,long,short)
- string
- class
- enum
- annotation
- 上述类型的数组
倘若使用了其他数据类型,编译器将会丢出一个编译错误,注意,声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时还应该注意到注解也可以作为元素的类型,也就是嵌套注解,下面的代码演示了上述类型的使用过程:
package com.zejian.annotationdemo; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; /** * created by wuzejian on 2017/5/19. * 数据类型使用demo */ @target(elementtype.type) @retention(retentionpolicy.runtime) @interface reference{ boolean next() default false; } public @interface annotationelementdemo { //枚举类型 enum status {fixed,normal}; //声明枚举 status status() default status.fixed; //布尔类型 boolean showsupport() default false; //string类型 string name()default ""; //class类型 class<?> testcase() default void.class; //注解嵌套 reference reference() default @reference(next=true); //数组类型 long[] value(); }
编译器对默认值的限制
编译器对元素的默认值有些过分挑剔。首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。其次,对于非基本类型的元素,无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值,这就是限制,没有什么利用可言,但造成一个元素的存在或缺失状态,因为每个注解的声明中,所有的元素都存在,并且都具有相应的值,为了绕开这个限制,只能定义一些特殊的值,例如空字符串或负数,表示某个元素不存在。
注解不支持继承
注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.annotation接口,这里我们反编译前面定义的dbtable注解
package com.zejian.annotationdemo; import java.lang.annotation.annotation; //反编译后的代码 public interface dbtable extends annotation { public abstract string name(); }
虽然反编译后发现dbtable注解继承了annotation接口,请记住,即使java的接口可以实现多继承,但定义注解时依然无法使用extends关键字继承@interface。
快捷方式
所谓的快捷方式就是注解中定义了名为value的元素,并且在使用该注解时,如果该元素是唯一需要赋值的一个元素,那么此时无需使用key=value的语法,而只需在括号内给出value元素所需的值即可。这可以应用于任何合法类型的元素,记住,这限制了元素名必须为value,简单案例如下
package com.zejian.annotationdemo; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; /** * created by zejian on 2017/5/20. */ //定义注解 @target(elementtype.field) @retention(retentionpolicy.runtime) @interface integervaule{ int value() default 0; string name() default ""; } //使用注解 public class quicklyway { //当只想给value赋值时,可以使用以下快捷方式 @integervaule(20) public int age; //当name也需要赋值时必须采用key=value的方式赋值 @integervaule(value = 10000,name = "money") public int money; }
java内置注解与其它元注解
接着看看java提供的内置注解,主要有3个,如下:
@override:用于标明此方法覆盖了父类的方法,源码如下
@target(elementtype.method) @retention(retentionpolicy.source) public @interface override { }
@deprecated:用于标明已经过时的方法或类,源码如下,关于@documented稍后分析:
@documented @retention(retentionpolicy.runtime) @target(value={constructor, field, local_variable, method, package, parameter, type}) public @interface deprecated { }
@suppresswarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告,其实现源码如下:
@target({type, field, method, parameter, constructor, local_variable}) @retention(retentionpolicy.source) public @interface suppresswarnings { string[] value(); }
其内部有一个string数组,主要接收值如下:
- deprecation:使用了不赞成使用的类或方法时的警告;
- unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (generics) 来指定集合保存的类型;
- fallthrough:当 switch 程序块直接通往下一种情况而没有 break 时的警告;
- path:在类路径、源文件路径等中有不存在的路径时的警告;
- serial:当在可序列化的类上缺少 serialversionuid 定义时的警告;
- finally:任何 finally 子句不能正常完成时的警告;
- all:关于以上所有情况的警告。
这个三个注解比较简单,看个简单案例即可:
//注明该类已过时,不建议使用 @deprecated class a{ public void a(){ } //注明该方法已过时,不建议使用 @deprecated() public void b(){ } } class b extends a{ @override //标明覆盖父类a的a方法 public void a() { super.a(); } //去掉检测警告 @suppresswarnings({"uncheck","deprecation"}) public void c(){ } //去掉检测警告 @suppresswarnings("uncheck") public void d(){ } }
前面我们分析了两种元注解,@target和@retention,除了这两种元注解,java还提供了另外两种元注解,@documented和@inherited,下面分别介绍:
@documented 被修饰的注解会生成到javadoc中
/** * created by zejian on 2017/5/20. */ @documented @target(elementtype.type) @retention(retentionpolicy.runtime) public @interface documenta { } //没有使用@documented @target(elementtype.type) @retention(retentionpolicy.runtime) public @interface documentb { } //使用注解 @documenta @documentb public class documentdemo { public void a(){ } }
使用javadoc命令生成文档:
如下:
可以发现使用@documented元注解定义的注解(@documenta)将会生成到javadoc中,而@documentb则没有在doc文档中出现,这就是元注解@documented的作用。
@inherited 可以让注解被继承,但这并不是真的继承,只是通过使用@inherited,可以让子类class对象使用getannotations()获取父类被@inherited修饰的注解,如下:
@inherited @documented @target(elementtype.type) @retention(retentionpolicy.runtime) public @interface documenta { } @target(elementtype.type) @retention(retentionpolicy.runtime) public @interface documentb { } @documenta class a{ } class b extends a{ } @documentb class c{ } class d extends c{ } //测试 public class documentdemo { public static void main(string... args){ a instancea=new b(); system.out.println("已使用的@inherited注解:"+arrays.tostring(instancea.getclass().getannotations())); c instancec = new d(); system.out.println("没有使用的@inherited注解:"+arrays.tostring(instancec.getclass().getannotations())); } /** * 运行结果: 已使用的@inherited注解:[@com.zejian.annotationdemo.documenta()] 没有使用的@inherited注解:[] */ }
注解与反射机制
前面经过反编译后,我们知道java所有注解都继承了annotation接口,也就是说 java使用annotation接口代表注解元素,该接口是所有annotation类型的父接口。同时为了运行时能准确获取到注解的相关信息,java在java.lang.reflect 反射包下新增了annotatedelement接口,它主要用于表示目前正在 vm 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息,如反射包的constructor类、field类、method类、package类和class类都实现了annotatedelement接口,它简要含义如下(更多详细介绍可以看 深入理解java类型信息(class对象)与反射机制):
- class:类的class对象定义
- constructor:代表类的构造器定义
- field:代表类的成员变量定义
- method:代表类的方法定义
- package:代表类的包定义
下面是annotatedelement中相关的api方法,以上5个类都实现以下的方法
返回值 | 方法名称 | 说明 |
---|---|---|
<a extends annotation> | getannotation(class<a> annotationclass) | 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。 |
annotation[] | getannotations() | 返回此元素上存在的所有注解,包括从父类继承的 |
boolean | isannotationpresent(class<? extends annotation> annotationclass) | 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。 |
annotation[] | getdeclaredannotations() | 返回直接存在于此元素上的所有注解,注意,不包括父类的注解,调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组 |
简单案例演示如下:
package com.zejian.annotationdemo; import java.lang.annotation.annotation; import java.util.arrays; /** * created by zejian on 2017/5/20. */ @documenta class a{ } //继承了a类 @documentb public class documentdemo extends a{ public static void main(string... args){ class<?> clazz = documentdemo.class; //根据指定注解类型获取该注解 documenta documenta=clazz.getannotation(documenta.class); system.out.println("a:"+documenta); //获取该元素上的所有注解,包含从父类继承 annotation[] an= clazz.getannotations(); system.out.println("an:"+ arrays.tostring(an)); //获取该元素上的所有注解,但不包含继承! annotation[] an2=clazz.getdeclaredannotations(); system.out.println("an2:"+ arrays.tostring(an2)); //判断注解documenta是否在该元素上 boolean b=clazz.isannotationpresent(documenta.class); system.out.println("b:"+b); /** * 执行结果: a:@com.zejian.annotationdemo.documenta() an:[@com.zejian.annotationdemo.documenta(), @com.zejian.annotationdemo.documentb()] an2:@com.zejian.annotationdemo.documentb() b:true */ } }
运行时注解处理器
了解完注解与反射的相关api后,现在通过一个实例(该例子是博主改编自《tinking in java》)来演示利用运行时注解来组装数据库sql的构建语句的过程
/** * created by wuzejian on 2017/5/18. * 表注解 */ @target(elementtype.type)//只能应用于类上 @retention(retentionpolicy.runtime)//保存到运行时 public @interface dbtable { string name() default ""; } /** * created by wuzejian on 2017/5/18. * 注解integer类型的字段 */ @target(elementtype.field) @retention(retentionpolicy.runtime) public @interface sqlinteger { //该字段对应数据库表列名 string name() default ""; //嵌套注解 constraints constraint() default @constraints; } /** * created by wuzejian on 2017/5/18. * 注解string类型的字段 */ @target(elementtype.field) @retention(retentionpolicy.runtime) public @interface sqlstring { //对应数据库表的列名 string name() default ""; //列类型分配的长度,如varchar(30)的30 int value() default 0; constraints constraint() default @constraints; } /** * created by wuzejian on 2017/5/18. * 约束注解 */ @target(elementtype.field)//只能应用在字段上 @retention(retentionpolicy.runtime) public @interface constraints { //判断是否作为主键约束 boolean primarykey() default false; //判断是否允许为null boolean allownull() default false; //判断是否唯一 boolean unique() default false; } /** * created by wuzejian on 2017/5/18. * 数据库表member对应实例类bean */ @dbtable(name = "member") public class member { //主键id @sqlstring(name = "id",value = 50, constraint = @constraints(primarykey = true)) private string id; @sqlstring(name = "name" , value = 30) private string name; @sqlinteger(name = "age") private int age; @sqlstring(name = "description" ,value = 150 , constraint = @constraints(allownull = true)) private string description;//个人描述 //省略set get..... }
上述定义4个注解,分别是@dbtable(用于类上)、@constraints(用于字段上)、 @sqlstring(用于字段上)、@sqlstring(用于字段上)并在member类中使用这些注解,这些注解的作用的是用于帮助注解处理器生成创建数据库表member的构建语句,在这里有点需要注意的是,我们使用了嵌套注解@constraints,该注解主要用于判断字段是否为null或者字段是否唯一。必须清楚认识到上述提供的注解生命周期必须为@retention(retentionpolicy.runtime),即运行时,这样才可以使用反射机制获取其信息。有了上述注解和使用,剩余的就是编写上述的注解处理器了,前面我们聊了很多注解,其处理器要么是java自身已提供、要么是框架已提供的,我们自己都没有涉及到注解处理器的编写,但上述定义处理sql的注解,其处理器必须由我们自己编写了,如下
package com.zejian.annotationdemo; import java.lang.annotation.annotation; import java.lang.reflect.field; import java.util.arraylist; import java.util.list; /** * created by zejian on 2017/5/13. * 运行时注解处理器,构造表创建语句 */ public class tablecreator { public static string createtablesql(string classname) throws classnotfoundexception { class<?> cl = class.forname(classname); dbtable dbtable = cl.getannotation(dbtable.class); //如果没有表注解,直接返回 if(dbtable == null) { system.out.println( "no dbtable annotations in class " + classname); return null; } string tablename = dbtable.name(); // if the name is empty, use the class name: if(tablename.length() < 1) tablename = cl.getname().touppercase(); list<string> columndefs = new arraylist<string>(); //通过class类api获取到所有成员字段 for(field field : cl.getdeclaredfields()) { string columnname = null; //获取字段上的注解 annotation[] anns = field.getdeclaredannotations(); if(anns.length < 1) continue; // not a db table column //判断注解类型 if(anns[0] instanceof sqlinteger) { sqlinteger sint = (sqlinteger) anns[0]; //获取字段对应列名称,如果没有就是使用字段名称替代 if(sint.name().length() < 1) columnname = field.getname().touppercase(); else columnname = sint.name(); //构建语句 columndefs.add(columnname + " int" + getconstraints(sint.constraint())); } //判断string类型 if(anns[0] instanceof sqlstring) { sqlstring sstring = (sqlstring) anns[0]; // use field name if name not specified. if(sstring.name().length() < 1) columnname = field.getname().touppercase(); else columnname = sstring.name(); columndefs.add(columnname + " varchar(" + sstring.value() + ")" + getconstraints(sstring.constraint())); } } //数据库表构建语句 stringbuilder createcommand = new stringbuilder( "create table " + tablename + "("); for(string columndef : columndefs) createcommand.append("\n " + columndef + ","); // remove trailing comma string tablecreate = createcommand.substring( 0, createcommand.length() - 1) + ");"; return tablecreate; } /** * 判断该字段是否有其他约束 * @param con * @return */ private static string getconstraints(constraints con) { string constraints = ""; if(!con.allownull()) constraints += " not null"; if(con.primarykey()) constraints += " primary key"; if(con.unique()) constraints += " unique"; return constraints; } public static void main(string[] args) throws exception { string[] arg={"com.zejian.annotationdemo.member"}; for(string classname : arg) { system.out.println("table creation sql for " + classname + " is :\n" + createtablesql(classname)); } /** * 输出结果: table creation sql for com.zejian.annotationdemo.member is : create table member( id varchar(50) not null primary key, name varchar(30) not null, age int not null, description varchar(150) ); */ } }
如果对反射比较熟悉的同学,上述代码就相对简单了,我们通过传递member的全路径后通过class.forname()方法获取到member的class对象,然后利用class对象中的方法获取所有成员字段field,最后利用field.getdeclaredannotations()遍历每个field上的注解再通过注解的类型判断来构建建表的sql语句。这便是利用注解结合反射来构建sql语句的简单的处理器模型,是否已回想起hibernate?
java 8中注解增强
元注解@repeatable
元注解@repeatable是jdk1.8新加入的,它表示在同一个位置重复相同的注解。在没有该注解前,一般是无法在同一个类型上使用相同的注解的
//java8前无法这样使用 @filterpath("/web/update") @filterpath("/web/add") public class a {}
java8前如果是想实现类似的功能,我们需要在定义@filterpath注解时定义一个数组元素接收多个值如下
@target(elementtype.type) @retention(retentionpolicy.runtime) public @interface filterpath { string [] value(); } //使用 @filterpath({"/update","/add"}) public class a { }
但在java8新增了@repeatable注解后就可以采用如下的方式定义并使用了
package com.zejian.annotationdemo; import java.lang.annotation.*; /** * created by zejian on 2017/5/20. */ //使用java8新增@repeatable原注解 @target({elementtype.type,elementtype.field,elementtype.method}) @retention(retentionpolicy.runtime) @repeatable(filterpaths.class)//参数指明接收的注解class public @interface filterpath { string value(); } @target(elementtype.type) @retention(retentionpolicy.runtime) @interface filterpaths { filterpath[] value(); } //使用案例 @filterpath("/web/update") @filterpath("/web/add") @filterpath("/web/delete") class aa{ }
我们可以简单理解为通过使用@repeatable后,将使用@filterpaths注解作为接收同一个类型上重复注解的容器,而每个@filterpath则负责保存指定的路径串。为了处理上述的新增注解,java8还在annotatedelement接口新增了getdeclaredannotationsbytype() 和 getannotationsbytype()两个方法并在接口给出了默认实现,在指定@repeatable的注解时,可以通过这两个方法获取到注解相关信息。但请注意,旧版api中的getdeclaredannotation()和 getannotation()是不对@repeatable注解的处理的(除非该注解没有在同一个声明上重复出现)。注意getdeclaredannotationsbytype方法获取到的注解不包括父类,其实当 getannotationsbytype()方法调用时,其内部先执行了getdeclaredannotationsbytype方法,只有当前类不存在指定注解时,getannotationsbytype()才会继续从其父类寻找,但请注意如果@filterpath和@filterpaths没有使用了@inherited的话,仍然无法获取。下面通过代码来演示:
/** * created by zejian on 2017/5/20. */ //使用java8新增@repeatable原注解 @target({elementtype.type,elementtype.field,elementtype.method}) @retention(retentionpolicy.runtime) @repeatable(filterpaths.class) public @interface filterpath { string value(); } @target(elementtype.type) @retention(retentionpolicy.runtime) @interface filterpaths { filterpath[] value(); } @filterpath("/web/list") class cc { } //使用案例 @filterpath("/web/update") @filterpath("/web/add") @filterpath("/web/delete") class aa extends cc{ public static void main(string[] args) { class<?> clazz = aa.class; //通过getannotationsbytype方法获取所有重复注解 filterpath[] annotationsbytype = clazz.getannotationsbytype(filterpath.class); filterpath[] annotationsbytype2 = clazz.getdeclaredannotationsbytype(filterpath.class); if (annotationsbytype != null) { for (filterpath filter : annotationsbytype) { system.out.println("1:"+filter.value()); } } system.out.println("-----------------"); if (annotationsbytype2 != null) { for (filterpath filter : annotationsbytype2) { system.out.println("2:"+filter.value()); } } system.out.println("使用getannotation的结果:"+clazz.getannotation(filterpath.class)); /** * 执行结果(当前类拥有该注解filterpath,则不会从cc父类寻找) 1:/web/update 1:/web/add 1:/web/delete ----------------- 2:/web/update 2:/web/add 2:/web/delete 使用getannotation的结果:null */ } }
从执行结果来看如果当前类拥有该注解@filterpath,则getannotationsbytype方法不会从cc父类寻找,下面看看另外一种情况,即aa类上没有@filterpath注解
/** * created by zejian on 2017/5/20. */ //使用java8新增@repeatable原注解 @target({elementtype.type,elementtype.field,elementtype.method}) @retention(retentionpolicy.runtime) @inherited //添加可继承元注解 @repeatable(filterpaths.class) public @interface filterpath { string value(); } @target(elementtype.type) @retention(retentionpolicy.runtime) @inherited //添加可继承元注解 @interface filterpaths { filterpath[] value(); } @filterpath("/web/list") @filterpath("/web/getlist") class cc { } //aa上不使用@filterpath注解,getannotationsbytype将会从父类查询 class aa extends cc{ public static void main(string[] args) { class<?> clazz = aa.class; //通过getannotationsbytype方法获取所有重复注解 filterpath[] annotationsbytype = clazz.getannotationsbytype(filterpath.class); filterpath[] annotationsbytype2 = clazz.getdeclaredannotationsbytype(filterpath.class); if (annotationsbytype != null) { for (filterpath filter : annotationsbytype) { system.out.println("1:"+filter.value()); } } system.out.println("-----------------"); if (annotationsbytype2 != null) { for (filterpath filter : annotationsbytype2) { system.out.println("2:"+filter.value()); } } system.out.println("使用getannotation的结果:"+clazz.getannotation(filterpath.class)); /** * 执行结果(当前类没有@filterpath,getannotationsbytype方法从cc父类寻找) 1:/web/list 1:/web/getlist ----------------- 使用getannotation的结果:null */ } }
注意定义@filterpath和@filterpath时必须指明@inherited,getannotationsbytype方法否则依旧无法从父类获取@filterpath注解,这是为什么呢,不妨看看getannotationsbytype方法的实现源码:
//接口默认实现方法 default <t extends annotation> t[] getannotationsbytype(class<t> annotationclass) { //先调用getdeclaredannotationsbytype方法 t[] result = getdeclaredannotationsbytype(annotationclass); //判断当前类获取到的注解数组是否为0 if (result.length == 0 && this instanceof class && //判断定义注解上是否使用了@inherited元注解 annotationtype.getinstance(annotationclass).isinherited()) { // inheritable //从父类获取 class<?> superclass = ((class<?>) this).getsuperclass(); if (superclass != null) { result = superclass.getannotationsbytype(annotationclass); } } return result; }
新增的两种elementtype
在java8中 elementtype 新增两个枚举成员,type_parameter 和 type_use ,在java8前注解只能标注在一个声明(如字段、类、方法)上,java8后,新增的type_parameter可以用于标注类型参数,而type_use则可以用于标注任意类型(不包括class)。如下所示
//type_parameter 标注在类型参数上 class d<@parameter t> { } //type_use则可以用于标注任意类型(不包括class) //用于父类或者接口 class image implements @rectangular shape { } //用于构造函数 new @path string("/usr/bin") //用于强制转换和instanceof检查,注意这些注解中用于外部工具,它们不会对类型转换或者instanceof的检查行为带来任何影响。 string path=(@path string)input; if(input instanceof @path string) //用于指定异常 public person read() throws @localized ioexception. //用于通配符绑定 list<@readonly ? extends person> list<? extends @readonly person> @notnull string.class //非法,不能标注class import java.lang.@notnull string //非法,不能标注import
这里主要说明一下type_use,类型注解用来支持在java的程序中做强类型检查,配合第三方插件工具(如checker framework),可以在编译期检测出runtime error(如unsupportedoperationexception、nullpointerexception异常),避免异常延续到运行期才发现,从而提高代码质量,这就是类型注解的主要作用。总之java 8 新增加了两个注解的元素类型elementtype.type_use 和elementtype.type_parameter ,通过它们,我们可以把注解应用到各种新场合中。
ok~,关于注解暂且聊到这,实际上还有一个大块的知识点没详细聊到,源码级注解处理器,这个话题博主打算后面另开一篇分析。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。