1. Overview
1.概述
From the Java 8 release onwards, it’s possible to compile programs using the so-called Pluggable Type Systems – which can apply stricter checks than the ones applied by the compiler.
从Java 8版本开始,可以使用所谓的Pluggable Type Systems来编译程序–它可以应用比编译器应用更严格的检查。
We only need to use the annotations provided by the several Pluggable Type Systems available.
我们只需要使用几个可插拔类型系统所提供的注释。
In this quick article, we’ll explore the Checker Framework, courtesy of the University of Washington.
在这篇快速文章中,我们将探讨检查器框架,它由华盛顿大学提供。
2. Maven
2.Maven
To start working with the Checker Framework, we need to first add it into our pom.xml:
要开始使用检查器框架,我们需要首先将其添加到我们的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>
The latest version of the libraries can be checked on Maven Central.
库的最新版本可以在Maven Central上查看。
The first two dependencies contain the code of The Checker Framework while the latter is a custom version of the Java 8 classes, in which all types have been properly annotated by the developers of The Checker Framework.
前两个依赖包含The Checker Framework的代码,而后者是Java 8类的自定义版本,其中所有类型都被The Checker Framework的开发者正确注释了。
We then have to properly tweak the maven-compiler-plugin to use The Checker Framework as a pluggable Type System:
然后我们必须适当调整maven-compiler-plugin,将The Checker Framework作为可插入的Type System。
<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>
The main point here is the content of the <annotationProcessors> tag. Here we listed all the checkers that we want to run against our sources.
这里的重点是<annotationProcessors>标签的内容。这里我们列出了所有我们想针对我们的源运行的检查器。
3. Avoiding NullPointerExceptions
3.避免NullPointerExceptions
The first scenario in which The Checker Framework can help us is identifying the piece of codes where a NullPoinerException could originate:
检查器框架可以帮助我们的第一种情况是确定NullPoinerException可能起源的那段代码。
private static int countArgs(@NonNull String[] args) {
return args.length;
}
public static void main(@Nullable String[] args) {
System.out.println(countArgs(args));
}
In the above example, we declared with the @NonNull annotation that the args argument of countArgs() has to be not null.
在上面的例子中,我们用@NonNull注解声明了countArgs()的args参数必须为非空。
Regardless of this constraint, in main(), we invoke the method passing an argument that can indeed be null, because it’s been annotated with @Nullable.
不管这个约束,在main()中,我们调用方法时传递的参数确实可以是空的,因为它已经被注解为@Nullable。
When we compile the code, The Checker Framework duly warns us that something in our code could be wrong:
当我们编译代码时,检查器框架会适时地警告我们,我们的代码中可能存在一些错误。
[WARNING] /checker-plugin/.../NonNullExample.java:[12,38] [argument.type.incompatible]
incompatible types in argument.
found : null
required: @Initialized @NonNull String @Initialized @NonNull []
4. Proper Use of Constants as Enumerations
4.正确使用作为枚举的常量
Sometimes we use a series of constants as they were items of an enumeration.
有时我们使用一系列的常数,就像它们是一个枚举的项目。
Let’s suppose we need a series of countries and planets. We can then annotate these items with the @Fenum annotation to group all the constants that are part of the same “fake” enumeration:
假设我们需要一系列的国家和行星。然后我们可以用@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";
After that, when we write a method that should accept a String that is a “planet”, we can properly annotate the argument:
之后,当我们写一个应该接受一个 “星球 “的字符串的方法时,我们可以正确地注释参数。
void greetPlanet(@Fenum("planet") String planet){
System.out.println("Hello " + planet);
}
By error, we can invoke greetPlanet() with a string that hasn’t been defined as being a possible value for a planet, such:
根据错误,我们可以用一个没有被定义为行星的可能值的字符串来调用greetPlanet(),比如。
public static void main(String[] args) {
obj.greetPlanets(US);
}
The Checker Framework can spot the error:
检查器框架可以发现错误。
[WARNING] /checker-plugin/.../FakeNumExample.java:[29,26] [argument.type.incompatible]
incompatible types in argument.
found : @Fenum("country") String
required: @Fenum("planet") String
5. Regular Expressions
5.正则表达式
Let’s suppose we know a String variable has to store a regular expression with at least one matching group.
假设我们知道一个String变量必须存储一个至少有一个匹配组的正则表达式。
We can leverage the Checker Framework and declare such variable like that:
我们可以利用检查器框架,像这样声明这样的变量。
@Regex(1) private static String FIND_NUMBERS = "\\d*";
This is obviously a potential error because the regular expression we assigned to FIND_NUMBERS does not have any matching group.
这显然是一个潜在的错误,因为我们分配给FIND_NUMBERS的正则表达式并没有任何匹配组。
Indeed, the Checker Framework will diligently inform us about our error at compile time:
事实上,检查器框架会在编译时勤奋地通知我们的错误。
[WARNING] /checker-plugin/.../RegexExample.java:[7,51] [assignment.type.incompatible]
incompatible types in assignment.
found : @Regex String
required: @Regex(1) String
6. Conclusion
6.结论
The Checker Framework is a useful tool for developers that want to go beyond the standard compiler and improve the correctness of their code.
检查器框架对于那些希望超越标准编译器并提高其代码正确性的开发人员来说是一个有用的工具。
It’s able to detect, at compile time, several typical errors that can usually only be detected at runtime or even halt compilation by raising a compilation error.
它能够在编译时检测到几个典型的错误,这些错误通常只能在运行时检测到,甚至可以通过引发编译错误来停止编译工作。
There’re many more standard checks than what we covered in this article; check out the checks available in The Checker Framework official manual here, or even write your own.
除了我们在本文中涉及的内容外,还有许多标准检查;请查看检查器框架官方手册这里中提供的检查,甚至可以自己编写。
As always, the source code for this tutorial, with some more examples, can be found over on GitHub.
一如既往,本教程的源代码,以及一些更多的例子,可以在GitHub上找到over。