1. 概要
Javaは絶えず進化し、JDKに新しい機能を追加しています。 また、APIでこれらの機能を使用する場合は、ダウンストリームの依存関係でJDKバージョンをアップグレードする必要があります。
互換性を維持するために、新しい言語機能の使用を待たなければならない場合があります。
ただし、このチュートリアルでは、マルチリリースJAR(MRJAR)と、異なるJDKバージョンと互換性のある実装を同時に含める方法について学習します。
2. 簡単な例
うるう年をチェックするメソッドを持つDateHelperというユーティリティクラスを見てみましょう。 JDK 7を使用して作成され、JRE7以降で実行するように構築されていると仮定します。
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には、日付を解析するためのより簡潔な方法があることがわかっています。 そこで、これを利用してロジックを書き直したいと思います。 このためには、JDK8以降に切り替える必要があります。 ただし、これは、モジュールが最初に作成されたJRE7で動作を停止することを意味します。
そして、絶対に必要でない限り、それが起こらないようにします。
3. マルチリリースjarファイル
Java 9の解決策は、元のクラスをそのままにして、代わりに新しいJDKを使用して新しいバージョンを作成し、それらを一緒にパッケージ化することです。 実行時に、JVM(バージョン9以降)はこれら2つのバージョンのいずれかを呼び出し、JVMがサポートする最高のバージョンを優先します。
たとえば、MRJARに同じクラスのJavaバージョン7(デフォルト)、9、および10が含まれている場合、JVM 10+はバージョン10を実行し、JVM9はバージョン9を実行します。 どちらの場合も、そのJVMにより適切なバージョンが存在するため、デフォルトバージョンは実行されません。
クラスの新しいバージョンのpublic定義は、元のバージョンと完全に一致する必要があることに注意してください。 つまり、新しいバージョン専用の新しいパブリックAPIを追加することは許可されていません。
4. フォルダ構造
Javaのクラスは名前でファイルに直接マップされるため、同じ場所にDateHelperの新しいバージョンを作成することはできません。 したがって、それらを別のフォルダーに作成する必要があります。
javaと同じレベルにフォルダjava9を作成することから始めましょう。 その後、パッケージフォルダ構造を保持したDateHelper。javaファイルのクローンを作成し、 java 9:に配置します。
src/
main/
java/
com/
baeldung/
multireleaseapp/
App.java
DateHelper.java
java9/
com/
baeldung/
multireleaseapp/
DateHelper.java
MRJAR をまだサポートしていない一部のIDEは、重複するDateHelper。javaクラスに対してエラーをスローする場合があります。
別のチュートリアルでは、これを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バージョンをインストールする必要がないことを意味します。
JDK9以降を使用してクラスをコンパイルしてみましょう。
まず、Java7プラットフォーム用の古いコードをコンパイルします。
javac --release 7 -d classes src\main\java\com\baeldung\multireleaseapp\*.java
次に、Java9プラットフォーム用の新しいコードをコンパイルします。
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. テスト
最後に、jarをJava7または8に対してテストしてみましょう。
> 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
次に、Java9以降に対してjarを再度テストしてみましょう。
> 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のコードベースはGitHubで利用できます。