Java のバージョン間クロスコンパイル時の標準ライブラリについて (JDK 8 で Java 7 向けにコンパイルする場合など)
追記 (2017-10-21)
Java SE 9 のリリースにより、より便利な --release
フラグを使えるようになりました! 下記ページを参考にしてください。
元の内容
時代は Java 8 ですが、ライブラリなどはまだ Java 7 向けだったり Java 6 向けにビルドすることも多いと思います。
そんなとき、javac
の -source
オプションや -target
オプションを使ってターゲットバージョンを指定しますね。 Gradle を使っていれば次のような感じですね。
sourceCompatibility = 1.7 targetCompatibility = 1.7
ここで、何も気にせず JDK 8 でコンパイルしていると Java SE 7 環境には存在しない Java API のメソッドを使っていることに気付かず、Java SE 7 環境で実行するとエラーが発生する、ということが起こりえます。 そういうことが起こらないように、コンパイル時には対象となる Java バージョンに応じた標準ライブラリを見ましょう、という話です。
ブートストラップクラスパスの設定に関する警告
上のような設定を書いて javac
コマンド (Gradle を使っていたら gradle build
コマンド) でコンパイルを実行すると、次のような警告が発生すると思います。
警告: [options] ブートストラップ・クラスパスが-source 1.7と一緒に設定されていません
これは、ターゲットとなる Java のバージョンとして 1.7 が指定されているにもかかわらず、コンパイル時に参照される標準ライブラリが指定されていない *1 という警告です。 コンパイル自体は問題なく完了しますが、例えば Java SE 8 で導入された Java API のメソッドを使用している場合、コンパイルはできても Java SE 7 環境で実行するとメソッドがなくてエラーになる、ということが起こりえるため警告が出されるようです。
実行時ではなくコンパイル時にエラーが出るように、ターゲットとなる Java バージョンの Java API を参照してコンパイルするように変更すべきです。
Oracle によるドキュメントでは次のような感じで書かれています。
You must specify the
-bootclasspath
option to specify the correct version of the bootstrap classes (the rt.jar library). If not, then the compiler generates a warning:javac -source 1.7 OldCode.java warning: [options] bootstrap class path not set in conjunction with -source 1.7If you do not specify the correct version of bootstrap classes, then the compiler uses the old language rules (in this example, it uses version 1.7 of the Java programming language) combined with the new bootstrap classes, which can result in class files that do not work on the older platform (in this case, Java SE 7) because reference to nonexistent methods can get included.
javac
javac
コマンドを直接実行する場合、-bootclasspath
を適切に設定します。
Eclipse の場合
Eclipse の場合、プロジェクトのプロパティを変更してビルド時の標準ライブラリを指定できます。
「Java Build Path」 の 「Libraries」 の中を見ると、デフォルトではワークスペースのデフォルト JRE のライブラリが使われるような設定になっていると思いますが、それを変更してやればよいです。 指定するバージョンの JRE (または JDK) は予めインストールしておく必要があります。
Gradle の場合
Gradle を使う場合は、上のページにあるように次のようなコードをビルドスクリプトに追加してやればよいです。 対象となるバージョンの JDK をインストールしておいて、そのパスを環境変数 JDK7_HOME
に入れておけばビルド時にその中の rt.jar などが参照されるようになります。
tasks.withType(JavaCompile) { doFirst { if (sourceCompatibility == '1.7' && System.env.JDK7_HOME != null) { options.fork = true options.bootClasspath = "$System.env.JDK7_HOME/jre/lib/rt.jar" options.bootClasspath += "$File.pathSeparator$System.env.JDK7_HOME/jre/lib/jsse.jar" // use the line above as an example to add jce.jar // and other specific JDK jars } } }
とはいえ面倒な感じですし普段の開発は Eclipse なりなんなりでするでしょうから、そっちで設定を行って Gradle は素の状態でいいのではないかなーと思う次第です。