Compiling for Java 8 and Java 9
Saturday, April 15, 2017 |In a project I’m working on for my book, I need to share classes between two applications. One, an Android project, requires Java 8. The other, a desktop JavaFX application, needs to run under Java 9, complete with module support. The problem with this is that the Maven tooling isn’t quite ready for Java 9, so it’s not as simple as I would like. I have, however, found a solution that seems to work.
In this setup, I have three projects: the shared module, the Android project, and the JavaFX project. The shared module looks roughly like this:
1
2
3
4
5
6
7
8
9
src
main
java
module-info.java
com
steeplesoft
foo
model
Message.java
Our module is defined this way:
1
2
3
module foo.shared {
exports com.steeplesoft.foo.model;
}
We want to compile everything so that the bytecode is usable via the Java 8 JVM, but module-info.java won’t compile for Java 8. Here, we apply some Maven magic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<excludes>
<exclude>**/module-info.java</exclude>
</excludes>
<source>1.8</source>
<target>1.8</target>
</configuration>
</execution>
<execution>
<id>module-infos</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<excludes>
<exclude>**/*</exclude>
</excludes>
<includes>
<include>**/module-info.java</include>
</includes>
<source>1.9</source>
<target>1.9</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
We accomplish our goal by configuring the Maven compiler plugin with two different executions.
In the first, we compile everything using a source
and target
of 1.8
. For this pass, though,
we exclude module-info.java
. For the next pass, we compile with a source
and target
of 9
. We also exclude everything so that our Java 8-compatible .class
files aren’t
overwritten, but we also explicitly include
our module-info.java
source file. Once this
build runs, we’ll have mostly Java 8 .class
files in our output directory, with a single
Java 9-specific file, module-info.class
. The normal Maven process than jars everything up
and installs it in our local repository.
Over in our Android application, we can then declare the dependency on this jar:
1
2
3
4
5
6
7
8
9
10
repositories {
jcenter()
mavenCentral()
mavenLocal()
}
// ...
dependencies {
compile 'com.steeplesoft:foo-shared:1.0-SNAPSHOT'
}
We can then import and use our model class, Message
, and build and run our application.
On the Java 9 side, we declare a dependency on our module in the POM, and can configure our application’s Java module like this:
1
2
3
4
5
6
7
module foo.desktop {
requires foo.shared;
requires javafx.graphics;
requires javafx.controls;
requires javafx.fxml;
}
And we can successfully build and run our JavaFX application. Both Android and Java 9 are, as they say, fat, dumb and happy, and we have our shared code in a single project that can be used by both. It’s a bit more XML than we’d probably like, but, as Maven users, we’re probably used to that by now. :)