第07部分:枚举和注解

Java 有两种特殊形式的类和接口,在类型系统中扮演着特定的角色。这两种类型是枚举类型(enumerated type)和注解类型(annotation type),一般直接称为枚举和注解。


枚举

枚举是类的变种,功能有限,而且允许使用的值很少。

例如,假设我们想定义一个类型,表示三原色红绿蓝,而且希望这个类型只有这三个可以使用的值。我们可以使用 enum 关键字定义这个类型:

PrimaryColor 类型的实例可以按照静态字段的方式引用:PrimaryColor.RED、PrimaryColor.GREEN 和 PrimaryColor.BLUE。

在其他语言中,例如 C++,枚举一般使用整数常量实现,但 Java 采用的方式能提供更好的类型安全性和灵活性。例如,因为枚举是特殊的类,所以可以拥有成员,即字段和方法。如果字段或方法有主体,那么实例列表后面必须加上分号。

例如,假设我们要定义一个枚举,包含前几个正多边形(等边等角的形状),而且想为这些形状指定一些属性(在方法中指定)。我们可以使用接收一个参数的枚举实现这个需求,如下所示:

参数(在这个例子中只有一个参数)传入构造方法,创建单个枚举实例。因为枚举实例由Java 运行时创建,而且在外部不能实例化,所以把构造方法声明为私有方法。

枚举有些特殊的特性:

• 都(隐式)扩展 java.lang.Enum 类;

• 不能泛型化;

• 可以实现接口;

• 不能被扩展;

• 如果枚举中的所有值都有实现主体,那么只能定义为抽象方法;• 只能有一个私有(或使用默认访问权限)的构造方法。


注解

注解是一种特殊的接口。如名称所示,其作用是注解 Java 程序的某个部分。例如 @Override 注解。在前面的一些示例中你可能见到过这个注解,想知道它有什么作用。简单来说,什么作用也没有。这个答案或许会让你感到诧异。

说得稍微详细(也轻率)一点儿,注解没有直接作用,@Override 只是为注解的方法提供额外的信息,注明这个方法覆盖了超类中的方法。

注解能为编译器和集成开发环境(Integrated Development Environment,IDE)提供有用的提示。如果开发者把方法的名称拼写错了,而这个方法本来是要覆盖超类的方法,那么,在这个名称拼错的方法上使用 @Override 注解,可以提醒编译器什么地方出错了。

注解不能改变程序的语义,只能提供可选的元信息。严格说来,这意味着注解不能影响程序的执行,只能为编译器和其他预执行阶段提供信息。

Java 平台在 java.lang 中定义了为数不多的基本注解。一开始只支持 @Override、@Deprecated 和 @SuppressWarnings,这三个注解的作用分别是:注明方法是覆盖的,注明方法废弃了,以及静默编译器生成的警告。

后来,Java 7 增加了 @SafeVarargs(为变长参数方法提供增强的警告静默功能),Java 8 增加了 @FunctionalInterface。@FunctionalInterface 表示接口可以用作 lambda 表达式的目标。这是个很有用的标记注解,但不是必须使用的,后文会介绍。

和普通的接口相比,注解有些特殊的特性:

都(隐式)扩展 java.lang.annotation.Annotation 接口;

不能泛型化;

不能扩展其他接口;

只能定义没有参数的方法;

不能定义会抛出异常的方法;

方法的返回类型有限制;

方法可以有一个默认返回值。



自定义注解

自定义在自己的代码中使用的注解类型没什么难度。开发者可以使用 @interface 关键字定义新的注解类型,与定义类和接口的方式差不多。

自定义注解的关键是使用“元注解”。元注解是特殊的注解,用来注解新(自定义)注解类型的定义。

元注解在 java.lang.annotation 包中定义。开发者使用元注解指定新的注解类型能在哪里使用,以及编译器和运行时如何处理注解。

创建新的注解类型时,必须使用两个基本的元注解——@Target 和 @Retention。这两个注解接受的值都在枚举中定义。

@Target 元注解指明自定义的新注解能在 Java 源码的什么地方使用。可用的值在枚举ElementType 中 定 义, 包 括:TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、LOCAL_VARIABLE、ANNOTATION_TYPE、PACKAGE、TYPE_PARAMETER 和 TYPE_USE。

另一个元注解 @Retention 指明 javac 和 Java 运行时如何处理自定义的注解类型。可使用的值有三个,在枚举 RetentionPolicy 中定义。

• SOURCE

使用这个保留原则的注解,编译时会被 javac 丢弃。

• CLASS

表示注解会出现在类文件中,但运行时 JVM 无法访问。这个值很少使用,但有时会在JVM 字节码的离线分析工具中见到。

• RUNTIME

表示用户的代码在运行时(使用反射)能访问这个注解。

下面看个示例。这是个简单的注解,名为 @Nickname。开发者使用这个注解为方法指定一个昵称,运行时使用反射可以找到这个方法。

定义注解要做的就这么多——先指明注解能出现在哪里,然后是保留原则,最后是注解的名称。因为我们要给一个方法起昵称,所以还要在这个注解上定义一个方法。撇开这一点,自定义注解是个非常简单的任务。

除了两个基本的元注解之外,还有两个元注解:@Inherited 和 @Documented。实际使用中很少见到这两个注解,它们的详细说明参见 Java 平台的文档。


类型注解

Java 8 为枚举 ElementType 添加了两个新值:TYPE_PARAMETER 和 TYPE_USE。添加这两个值后,注解能在以前不能出现的地方使用了,例如使用类型的所有地方。现在,开发者可以编写如下的代码:

      @NotNull String safeString = getMyString();

@NotNull 传达的额外类型信息可在特殊的类型检查程序中使用,用于检测问题(对这个例子来说,可能抛出 NullPointerException 异常),还能执行额外的静态分析。Java 8 基本版自带了一些插入式类型检查程序,还提供了一个框架,开发者和库的作者可以使用这个框架自己编写类型检查程序。

你可能感兴趣的:(第07部分:枚举和注解)