注解


注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。

注解在一定程度上是在把元数据与源代码文件结合在一起,而不是保存在外部文档中这一大的趋势之下所催生的。

注解是众多引入到Java SE5中的重要的语言变化之一。它们可以提供用来完整地描述程序所需的信息,而这些信息是无法用Java来表达的。因此注解使得我们能够以将由编译器来测试和验证的格式,存储有关程序的额外信息。注解可以用来生成描述符文件,甚至是新的类定义,并且有助于减轻编写“样板”代码的负担。通过使用注解,我们可以将这些元数据保存在Java源代码中,并利用annotation API为自己的注解构造处理工具,同时注解的优点还包括:更加干净易读的代码以及编译期类型检査等。虽然Java SE5预先定义了一些元数据,但一般来说,主要还是需要程序员自己添加新的注解,并且按自己的方式使用它们。

注解的语法比较简单,除了@符号的使用之外,它基本与Java固有的语法一致。Java SE5内置了三种定义在java.lang中的注解:

  1. @Override,表示当前的方法定义将覆盖超类中的方法。如果你不小心拼写写错误,或者方法签名对不上被覆盖的方法,编译器就会发出错误提示。
  2. @Deprecated,如果程序员使用了注解为它的元素,那么编译器会发出警告信息。
  3. @SuppressWarnings,关闭不当的编译器警告信息。在Java SE5之前的版本中,也可以使用该注解,不过会被忽略不起作用。

Java还令外提供了四种注解,专门负责新注解的创建。稍后我们将学习它们。

每当你创建描述符性质的类或接口时,一但其中包含了重复性的工作,那就可以考虑使用注解来简化与自动化该过程。例如在Enterprise JavaBean(EJB)中存在许多额外的工作,EJB 3.0就是使用注解消除了它们。

注解是真正的语言级的概念,一旦构造出来就享有编译期的类型检査保护。注解(annotation)是在实际的源码级别保存所有的信息,而不是某种注释性的文字(comment),这使得代码更整洁,且便于维护。通过使用扩展的annotation API,或外部的字节码工具类库(稍后你将会看到),程序员拥有对源代码以及字节码强大的检査与操作能力。

基本语法

在下面的例子中,使用@Test对t()方法进行注解。该注解本身并不做任何事情,但是编译器要确保在其构造路径上必须有@Test注解的定义。

public class Test{
	@Test3
	void t(){
	}
}

被注解的方法与其他的方法没有区別。在这个例子中,注解@Test可以与任何修饰符共同作用于方法。从语法的角度来看,注解的使用方式几乎与修饰符的使用一模一样。

定义注解

下面就是前例中用到的注解@Test的定义。可以看到,注解的定义看起来很像接口的定义。事实上与其他任何java接口一样,注解也将会编译成class文件。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Test3{}

除了@符号以外,@Test3的定义很像一个空的接口。定义注解时,会需要一些元注解(meta-annotation),如@Target和@Retention。@Target用来定义你的注解将应用于什么地方(例如是一个方法或者一个域)。@Retention用来定义该注解在哪一个级別可用,在源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)。

在注解中,一般都会包含一些元素以表示某些值。当分析处理注解时,程序或工具可以利用这些值。注解的元素看起来就像接口的方法,唯一的区别是你可以为其指定默认值。

没有元素的注解称为标记注解(marker annotation),例如上例中的@Test3。

下面是一个筒单的例子:

public class Test{
	@UseCase(id = 1,description = "f1")
	public void f1(){}
	
	@UseCase(id = 2)
	public void f2(){}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface UseCase{
	public int id();
	public String description() default "no description";
}

注解的元素在使用时表现为名-值对的形式,并需要置于@UseCase声明之后的括号内。在f2()方法的注解中。注意,description元素有一个default值,并且在注解f2()方法时没有给出description的值,则该注解的处理器就会使用此元素的默认值。

元注解

Java目前只内置了三种标准注解(前面介绍过了),以及四种元注解。元注解专职负责注解其他的注解:
在这里插入图片描述

编写注解处理器

如果没有用来读取注解的工具,那注解也不会比注释更有用。使用注解的过程中,很重要的一个部分就是创建与使用**注解处理器*。 Java SE5扩展了反射机制的API,以帮助程序员构造这类工具。同时它还提供了一个外部工具apt帮助程序员解析带有注解的Java源代码。

下面是一个非常简单的注解处理器,我们将用它来读取Test类:

public class Test{
	@Usecase(id = 1,description = "f1")
	public void f1(){}
	@Usecase(id = 2)
	public void f2(){}
	@Usecase(id = 3,description = "f3")
	public void f3(){}
	@Usecase(id = 4,description = "f4")
	public void f4(){}
	
	public static void main(String[] args) {
		List<Integer> useCase = new ArrayList<Integer>();
		Collections.addAll(useCase,1,2,3,4);
		for (Method m : Test.class.getMethods()) {
			Usecase uc = m.getAnnotation(Usecase.class);
			if(uc != null){
				System.out.println(uc.id()+","+uc.description());
			}
			//do something ...
		}
	}
	//输出:
	//3,f3
	//2,no description
	//1,f1
	//4,f4
}

注解元素

标签@UseCase由UseCase.java定义,其中包含int元素id,以及一个String元素description。注解元素可用的类型如下所示:

  1. 所有基本类型(int,float, boolean等)
  2. String
  3. Class
  4. enum
  5. Annotation
  6. 以上类型的数组

如果你使用了其他类型,那编译器就会报错。注意,也不允许使用任何包装类型,不过由于自动打包的存在,这不算是什么限制了。注解也可以作为元素的类型,也就是说注解可以嵌套。

默认值限制

编译器对元素的默认值有些过分挑别。首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。

其次,对于非基本类型的元素,无论是在源代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为在每个注解的声明中,所有的元素都存在,并且都具有相应的值。为了绕开这个约束,我们只能自己定义一些特殊的值,例如空字符串或负数,以此表示某个元素不存在:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Usecase{
	public int id() default -1;
	public String description() default "no description";
}

注解不支持继承

不能使用关键字extends来继承某个@interface。这真是一个遗憾。如果注解允许继承的话,这将大大减少打字的工作量,并且使语法更整洁。在Java未来的版本中,似乎没有任何关于让注解支持继承的提案。


  1. 本文来源《Java编程思想(第四版)》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值