Checker 框架简介
1. 概述
从Java 8版本开始,可以使用所谓的Pluggable Type Systems 编译程序——它可以应用比编译器应用的检查更严格的检查。
我们只需要使用几个可用的Pluggable Type Systems提供的注解。
在这篇简短的文章中,我们将探索由华盛顿大学提供的 Checker 框架。
2. Maven
要开始使用 Checker 框架,我们需要先将其添加到我们的pom.xml 中:
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>jdk8</artifactId>
<version>2.3.2</version>
</dependency>
可以在Maven Central 上检查最新版本的库。
前两个依赖项包含Checker Framework的代码,而后者是Java 8类的自定义版本,其中所有类型都已由Checker Framework的开发人员正确注释。
然后我们必须适当地调整maven-compiler-plugin以使用Checker Framework作为可插入类型系统:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<Xmaxerrs>10000</Xmaxerrs>
<Xmaxwarns>10000</Xmaxwarns>
</compilerArguments>
<annotationProcessors>
<annotationProcessor>
org.checkerframework.checker.nullness.NullnessChecker
</annotationProcessor>
<annotationProcessor>
org.checkerframework.checker.interning.InterningChecker
</annotationProcessor>
<annotationProcessor>
org.checkerframework.checker.fenum.FenumChecker
</annotationProcessor>
<annotationProcessor>
org.checkerframework.checker.formatter.FormatterChecker
</annotationProcessor>
</annotationProcessors>
<compilerArgs>
<arg>-AprintErrorStack</arg>
<arg>-Awarns</arg>
</compilerArgs>
</configuration>
</plugin>
这里的重点是annotationProcessors标签的内容。在这里,我们列出了我们想要针对我们的源运行的所有检查器。
3. 避免 NullPointerExceptions
Checker 框架可以帮助我们的第一个场景是识别可能产生NullPoinerException的代码段:
private static int countArgs(@NonNull String[] args) {
return args.length;
}
public static void main(@Nullable String[] args) {
System.out.println(countArgs(args));
}
在上面的示例中,我们使用*@NonNull注释声明countArgs()的args*参数必须不为空。
不管这个约束如何,在main()中,我们调用传递一个确实可以为 null 的参数的方法,因为它已经用@Nullable进行了注释。
当我们编译代码时,Checker Framework会及时警告我们代码中的某些内容可能是错误的:
[WARNING] /checker-plugin/.../NonNullExample.java:[12,38] [argument.type.incompatible]
incompatible types in argument.
found : null
required: @Initialized @NonNull String @Initialized @NonNull []
4. 正确使用常量作为枚举
有时我们使用一系列常量,因为它们是枚举项。
假设我们需要一系列国家和行星。然后,我们可以使用*@Fenum*注释对这些项目进行注释,以对属于同一“假”枚举的所有常量进行分组:
static final @Fenum("country") String ITALY = "IT";
static final @Fenum("country") String US = "US";
static final @Fenum("country") String UNITED_KINGDOM = "UK";
static final @Fenum("planet") String MARS = "Mars";
static final @Fenum("planet") String EARTH = "Earth";
static final @Fenum("planet") String VENUS = "Venus";
之后,当我们编写一个应该接受作为“planet”的字符串的方法时,我们可以正确地注释参数:
void greetPlanet(@Fenum("planet") String planet){
System.out.println("Hello " + planet);
}
错误地,我们可以使用尚未定义为行星可能值的字符串调用greetPlanet() ,例如:
public static void main(String[] args) {
obj.greetPlanets(US);
}
Checker 框架可以发现错误:
[WARNING] /checker-plugin/.../FakeNumExample.java:[29,26] [argument.type.incompatible]
incompatible types in argument.
found : @Fenum("country") String
required: @Fenum("planet") String
5. 正则表达式
假设我们知道一个String变量必须存储一个至少有一个匹配组的正则表达式。
我们可以利用Checker 框架并像这样声明这样的变量:
@Regex(1) private static String FIND_NUMBERS = "\\d*";
这显然是一个潜在的错误,因为我们分配给FIND_NUMBERS的正则表达式没有任何匹配组。
事实上,Checker 框架会在编译时勤奋地通知我们我们的错误:
[WARNING] /checker-plugin/.../RegexExample.java:[7,51] [assignment.type.incompatible]
incompatible types in assignment.
found : @Regex String
required: @Regex(1) String