マルチリリースjarファイル

1. 概要

Javaは常に進化し、JDKに新しい機能を追加しています。 また、APIでこれらの機能を使用する場合、ダウンストリームの依存関係でJDKバージョンをアップグレードする必要があります。
時々、互換性を保つために、新しい言語機能の使用を待つ必要があります*。
ただし、このチュートリアルでは、Multi-Release JAR(MRJAR)について学習し、*異なるJDKバージョンと互換性のある実装を同時に含める方法を学習します。*

2. 簡単な例

link:/java-leap-year[leap years]をチェックするメソッドを持つ_DateHelper_erというユーティリティクラスを見てみましょう。 JDK 7を使用して作成され、JRE 7以降で実行するようにビルドされていると仮定します。
public class DateHelper {
    public static boolean checkIfLeapYear(String dateStr) throws Exception {
        logger.info("Checking for leap year using Java 1 calendar API ");

        Calendar cal = Calendar.getInstance();
        cal.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(dateStr));
        int year = cal.get(Calendar.YEAR);

        return (new GregorianCalendar()).isLeapYear(year);
    }
}
_checkIfLeapYear_メソッドは、テストアプリの_main_メソッドから呼び出されます。
public class App {
    public static void main(String[] args) throws Exception {
        String dateToCheck = args[0];
        boolean isLeapYear = DateHelper.checkIfLeapYear(dateToCheck);
        logger.info("Date given " + dateToCheck + " is leap year: " + isLeapYear);
    }
}
今日に早送りしましょう。
Java 8にはもっとlink:/java-8-date-time-intro [日付を解析する簡潔な方法]があることがわかっています。 そこで、これを利用してロジックを書き直したいと思います。 *このためには、JDK 8以降に切り替える必要があります。 ただし、それは、最初に作成されたJRE 7でモジュールが動作しなくなることを意味します。*
そして、絶対に必要な場合を除き、そのようなことは望ましくありません。

3. マルチリリースjarファイル

Java 9の解決策は、*元のクラスをそのままにして、代わりに新しいJDKを使用して新しいバージョンを作成し、それらを一緒にパッケージ化することです*。 実行時に、JVM(バージョン9以降)は、これら2つのバージョンのいずれかを呼び出します* JVMがサポートする最高のバージョンを優先します*。
たとえば、MRJARに同じバージョンのJavaバージョン7(デフォルト)、9および10が含まれている場合、JVM 10はバージョン10を実行し、JVM 9はバージョン9を実行します。 どちらの場合も、そのJVMにより適切なバージョンが存在するため、デフォルトバージョンは実行されません。
クラスの新しいバージョンの*パブリック定義は、元のバージョンと完全に一致する必要があることに注意してください*。 つまり、新しいバージョン専用の新しいパブリックAPIを追加することはできません。

4. フォルダー構造

Javaのクラスは名前によってファイルに直接マッピングされるため、同じ場所に_DateHelper_の新しいバージョンを作成することはできません。 したがって、別のフォルダーに作成する必要があります。
_java_と同じレベルに_java9_フォルダーを作成することから始めましょう。 その後、パッケージフォルダー構造を保持した_DateHelper.java_ファイルを複製して、_java9:_に配置します。
src/
    main/
        java/
            com/
                baeldung/
                    multireleaseapp/
                        App.java
                        DateHelper.java
        java9/
            com/
                baeldung/
                    multireleaseapp/
                        DateHelper.java
  • MRJAR *をまだサポートしていないIDEには、重複した_DateHelper.java_クラスに対してエラーをスローするものがあります。

    別のチュートリアルでhttps://maven.apache.org/plugins/maven-compiler-plugin/multirelease.html [これをMavenなどのビルドツールと統合する方法]を取り上げます。 とりあえず、基本に注目しましょう。

5. コードの変更

_java9_クローンクラスのロジックを書き直しましょう。
public class DateHelper {
    public static boolean checkIfLeapYear(String dateStr) throws Exception {
        logger.info("Checking for leap year using Java 9 Date Api");
        return LocalDate.parse(dateStr).isLeapYear();
    }
}
ここで、*クローンクラスのパブリックメソッドシグネチャは変更せず、内部ロジックのみを変更していることに注意してください。 同時に、新しいパブリックメソッドは追加していません。*
これらの2つのルールに従わないとjarの作成が失敗するため、これは非常に重要です。

6. Javaでのクロスコンパイル

クロスコンパイルは、以前のバージョンで実行するためにファイルをコンパイルできるJavaの機能です。 つまり、個別のJDKバージョンをインストールする必要はありません。
JDK 9以降を使用してクラスをコンパイルしましょう。
まず、Java 7プラットフォーム用の古いコードをコンパイルします。
javac --release 7 -d classes src\main\java\com\baeldung\multireleaseapp\*.java
次に、Java 9プラットフォーム用の新しいコードをコンパイルします。
javac --release 9 -d classes-9 src\main\java9\com\baeldung\multireleaseapp\*.java
_release_オプションは、JavaコンパイラとターゲットJREのバージョンを示すために使用されます。

7. MRJARの作成

最後に、バージョン9以降を使用してMRJARファイルを作成します。
jar --create --file target/mrjar.jar --main-class com.baeldung.multireleaseapp.App
  -C classes . --release 9 -C classes-9 .
  • _release_オプションの後にフォルダー名を指定すると、そのフォルダーの内容がバージョン番号の値でjarファイル内にパッケージ化されます。*

com/
    baeldung/
        multireleaseapp/
            App.class
            DateHelper.class
META-INF/
    versions/
        9/
            com/
                baeldung/
                    multireleaseapp/
                        DateHelper.class
    MANIFEST.MF
_MANIFEST.MF_ファイルには、これがMRJARファイルであることをJVMに知らせるためのプロパティセットがあります。
Multi-Release: true
その結果、JVMは実行時に適切なクラスをロードします。
古いJVMは、これがMRJARファイルであることを示す新しいプロパティを無視し、通常のJARファイルとして扱います。

8. テスト

最後に、Java 7または8に対してjarをテストしましょう。
> java -jar target/mrjar.jar "2012-09-22"
Checking for leap year using Java 1 calendar API
Date given 2012-09-22 is leap year: true
次に、jarをJava 9以降に対して再度テストしましょう。
> java -jar target/mrjar.jar "2012-09-22"
Checking for leap year using Java 9 Date Api
Date given 2012-09-22 is leap year: true

9. 結論

この記事では、簡単な例を使用してマルチリリースjarファイルを作成する方法を説明しました。
いつものように、multi-release-appのコードベースはhttps://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-9[GitHub上]で入手できます。