最近,一个队友在我们的Java 8代码中使用了以下功能:Matcher.replaceAll(Function replacer)。该函数是在Java 9中引入的,但是由于他使用的是较新的编译器,因此仅在JDK的rt.jar中找到了API函数,没有人注意到这在真正的Java 8环境中不起作用。兼容性设置正确设置,并且gradle子项目具有以下设置:
sourceCompatibility = 1.8
targetCompatibility = 1.8
当我第一次在Java 5代码中使用Java 6函数String.isEmpty
时,我遇到了非常相似的问题。
我可以采取什么措施来强制使用正确的API。因为它是一个共享库,所以我是否必须为此gradle子项目使用(以及安装,维护..)其他JDK,或者是否存在某种兼容的扫描程序,该扫描程序通过内置的jar运行并检查所有rt引用?
您已经注意到,这两个兼容性配置不考虑较旧版本的API-仅考虑语法,语义和生成的字节码。
您可以采取两种选择。一种是在计算机上安装JDK 8,然后配置Gradle以在编译项目时使用它。看起来像这样:
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.executable = "$java8Home/bin/javac"
options.bootstrapClasspath = files("$java8Home/jre/lib/rt.jar")
}
这里的缺点是,您首先需要安装JDK 8,并且由于它可能安装在不同的位置,所以您可能需要使用环境变量或属性对其进行配置(我已经将其命名为java8Home
。
但是,从Java 9开始,JDK现在知道以前版本的文档API,您可以通过新的--release
标志选择使用哪个API。如果使用未公开的API,这将行不通,但这意味着您可以使用任何版本的Java编译项目,并且仍然使生成的类与Java 8兼容。您可以这样操作:
tasks.withType(JavaCompile) {
if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
options.compilerArgs.addAll(['--release', '8'])
}
}
注意,只有在您仍然需要通过Java 8(通过JAVA_HOME
变量)支持运行Gradle的情况下,才使用'if'语句。如果仅使用更高版本,则可以将其删除,因此您始终要设置“ compilerArgs”。
对于某些版本的Java,可以在较新的JDK上构建Java代码以在较旧的JDK / JRE上运行。您已经发现--source
的--target
和javac
选项以及相应的Gradle设置。您可以做的另一件事是使用--bootclasspath
告诉javac
针对较旧版本的Java的运行时库进行编译。
由于您正在使用Gradle,请检查“ gradle-java-cross-compile-plugin”(https://github.com/nebula-plugins/gradle-java-cross-compile-plugin)。我找不到有关它的任何文档,但显然它处理的是--target
和--bootclasspath
。
话虽如此,我认为交叉编译Java不是一个好的解决方案。
我实际上建议您为您想要支持的所有Java版本安装带有JDK安装的持续集成(CI)服务器(例如Jenkins)。然后设置作业以构建代码和对每个Java版本运行单元测试。