核心技术Ⅱ:脚本、编译、注解处理:注解
跳到导航
跳到搜索
关于注解
注解是那些插入到源代码中使用其他工具可以对其进行处理的标签。 这些工具可以在源码层次上进行操作, 或者可以处理编译器在其中放置了注解的类文件。 注解不会改变程序的编译方式。 Java编译器对于包含注解和不包含注解的代码会生成相同的虚拟机指令。 为了能够受益于注解, 需要选择一个处理工具, 然后向你的处理工具具可以理解的代码中插入注解, 之后运用该处理工具处理代码。 注解的使用范围: 附属文件的自动生成, 例如部署描述符或者 bean信息类。 测试、 日志、 事务语义等代码的自动生成。
- 在Java中,注解是当作一个修饰符来使用的,是代码的一部分;
- 注解自身并不会做任何事情,它需要工具支持才会有用;
- (如:JUnit4 测试工具可能会调用所有标识为“@Test”的方法)
- 注解可以定义成包含元素的形式;
- (如:“@Test(ti111eout="10000")”)
- 每个注解都必须通过一个注解接口进行定义;
- (“@interface”声明创建了一个真正的Java接口)
使用注解
注解接口(@interface)
注解是由注解接口来定义的:
modifiers @interface AnnotafionName
{
elementDeclaration1
elementDeclaration2
...
}
每个元素声明都具有下面这种形式:
type elementName();
// 或
type elementName() default value;
- 所有的注解接口都隐式地扩展自“java.lang.annotation.Annotation”接口。
- (这是一个常规接口,不是一个注解接口)
- 无法扩展注解接口。
- (即:所有的注解接口都直接扩展自“java.lang.annotation.Annotation”)
- 不用提供那些实现了注解接口的类。
注解元素的类型:
- 基本类型(int、short,、long、byte、char、double、float或者boolean);
- String;
- Class;(具有一个可选的类型参数, 例如“Class <? extends MyClass>”)
- enum 类型;
- 注解类型;
- 由以上类型组成的数组;(由数组组成的数组不是合法的元素类型)
示例:
public @interface BugReport
{
enum Status { UNCONFIRMED, CONFIRMED, FIXED, NOTABUG };
boolean showStopper() default false;
String assignedTo() default "[none]";
Class<?> testCase() default Void.class;
Status status() default Status.UNCONFIRMED;
Reference ref() default @Reference();// an annotation type
String[] reportedBy();
}
注解语法
每个注解都具有下面这种格式:
@AnnotationName(elementName1=value1, elementName2=value2,...)
- 元素的顺序无关紧要。
- 如果某个元素的值并未指定,那么就使用声明的默认值。
- (默认值并不是和注解存储在一起的;相反地,它们是动态计算而来的)
- 两种简化注解:
- “标记注解”:注解中没有任何元素,或者所有元索都使用默认值;
@BugReport
- “单值注解”:有且仅有一个名为“value”的元素;
@ActionlistenerFor("ye11owButton")
- “标记注解”:注解中没有任何元素,或者所有元索都使用默认值;
- 一个项可以有多个注解。
- 如果注解的作者将其声明为可重复的(“@Repeatable”),那么可以多次重复使用同一个注解。
@BugReport(showStopper=true, reportedBy="Joe") @BugReport(reportedBy={"Harry", "Carl"}) public void checkRandomlnsertions()
- 如果元素值是一个数组,则值用括号括起来:
@BugReport(..., reportedBy={"Harry", "Carl"})
- 如果该元素具有单值,则可以忽略这些括号:
@BugReport(..., reportedBy="Joe") // same as {"Joe"}
- 一个注解元素可以是另一个注解,那么就可以创建出任意复杂的注解:
@BugReport(ref=@Reference(id="3352627"), ...)
- 因为注解是由编译器计算而来的,因此,所有元素值必须是编译期常量。
- 注解元素永远不能设置为“null”,甚至不允许其默认值为“null”。
- (可以使用其他的默认值,例如“""”或者“Void.class”)
- 不能在注解中引入循环依赖。
- (例如,因为“BugReport”具有一个注解类型为“Reference”的元素,那么“Reference”就不能再拥有一个类型为“BugReport”的元素)
注解用法
注解可以出现在许多地方,可以分为两类:“声明”和“类型”用法。
- 习惯(但不是必需)的做法,是将“声明”注解放牲到其他修饰符的前面,将“类型”用法注解放置到其他修饰符的后面。
private @NonNull String text; // Annotates the type use @Id private String userId; // Annotates the variable
- 注解的作者需要指定特定的注解可以出现在哪里。
- 如果一个注鲜可以同时应用于变量和类型用法,并且它确实被应用到了某个变量声明上,那么该变量和类型用法就都被注解了:
public User getUser(@NonNull String userId) // 如果 @NonNull 可以同时应用于参数和类型用法,那么 userId 参数就被注解了,而其参数类型是 @NonNull String
用法:声明注解
“声明”用法可以出现在,以下声明处:
- 包
- 类(包括“enum”)
- 接口(包括注解接口)
- 方法
- 构造器
- 实例域(包含“enum”常量)
- 局部变量
- 参数变扯
- 类型参数
- 对于类和接口,需要将注解放置在“class”和“interface”关键词的前面:
@Entity public class User{. . . }
- 对于变量, 需要将它们放置在类型的前面:
@SuppressWarnings("unchecked") List<User> users =. . .; public User getUser(@Param("id") String userld)
- 泛化类或方法中的类型参数,如下注解:
public class Cache<@Immutab1e V> { . . . }
- 包是在文件“package-info.java”中注解的:
/** Package-level Javadoc */ @GPL(version="3") package com.horstmann.corejava; import org.gnu.GPL;
- 对局部变责的注解只能在源码级别上进行处理。类文件并不描述局部变量。因此,所有的局部变量注解在编译完一个类的时候就会被遗弃掉。同样地,对包的注解不能在源码级别之外存在。
用法:类型用法
“类型”用法可以出现在,以下声明处:
- 与泛化类型引元一起使用:
List<@NonNul1 String> Comparator.<@NonNul1 String> reverseOrder()。
- 数组中的任何位置:
@NonNull String[][] words // words[i][j] 不为null String @NonNull [][] words // words 不为null String[] @NonNull [] words // words[i] 不为null
- 与超类和实现接口一起使用:
class Warning extends @Localized Message
- 与构造器调用一起使用:
new @Localized String( . . . )
- 与强制转型和“instanceof”检查一起使用:
- (只供外部工具使用,对强制转型和instanceof检查不会产生任何影响)
(@Localized String) text if (text instanceof @Localized String)
- 与异常规约一起使用:
public String read() throws @Localized IOException
- 与通配符和类型边界一起使用:
List<@Localized ? extends Message> List<? extends @Localized Message>
- 与方法和构造器引用一起使用:
@Localized Message::getText
有多种类型位笐是不能被注解的:
@NonNull String.class // ERROR: Cannot annotate class literal import java.lang.@NonNull String; // ERROR: Cannot annotate import
注解this
【???】
public class Point { pub1ic boo1ean equa1s (@ReadOn1y Point this, @ReadOn1y Object other){ . . . } }
第一个参数被称为接收器参数,它必须被命名为“this”,而它的类型就是要构建的类(“Point”)。
- 只能为方法而不能为构造器提供接收器参数。