Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

Java 9で「変わること」と、Javaのこれまで 第7回

Java 9で「変わること」と、Javaのこれまで 第7回

2018/04/18 14:00

 本連載では主に開発フェーズにフォーカスしJava 9の変更点を紹介してきましたが、最終回である今回は、開発後のリリースを中心に変更点を紹介します。また、すでにJava 10がリリースされましたが言語仕様にかかわる変更点はあまりありません。特に言語仕様やAPIの変更においてはJava 9が大きな分岐点であり、Java 10以降を理解する上でもJava 9での変更点を理解することは非常に大切なことと言えます。ただし、JDKのサポートに関して注意すべきことがあります。Oracle JDK 9はJDK 10のリリースと共にサポート期限を迎えてしまったため、Oracle JDKを利用する場合には、今後はJDK 10を利用する必要があります。JDKの位置づけはこれまでのJava 8以前と異なっていく予定であり、これらに関しては今後の連載であるJava 10の記事内で紹介します。

目次

Java 9以降とそれ以前のプログラムをサポートするライブラリを作成する

 これまでに紹介したとおり、Java 9での大きな変更点はモジュール機能です。この違いにより、Java 9以降のモジュール対応したコードはそれ以前の環境では動きません。こういった大きな分断は、これまでのJavaにおける変更にはありませんでした。

 そのため、Java 9以降への最適化か、それ以前で動くことを前提にした対応か、といった2つの選択肢になります。ただし、ライブラリ作成者がこのいずれか一方のみを選ばざるを得ない場合、これまでの環境優位性が大きく落ちてしまいます。

 そこで、Java 9からバージョンでの問題を回避するために、複数のバージョンに対応したクラスファイルやリソースファイルを1つのjarファイルに格納することができる「Multi-Release Jar Files」という機能が加わりました。

 その考え方は非常に単純であり、単にJava 9以前をサポートするjarファイルの中にJava 9以降向けのクラスファイルやリソースファイルも含めて1つのjarファイルとして配布するといったものです。また、この機能はモジュール機能のためだけにあるわけではなく、モジュール機能を使っていなくても利用できます。

Multi-Release Jar Filesの構造

 Multi-Release Jar Filesの構造をもったjarファイルを作るには、大きく分けて2つのルールを守る必要があります。

 1つめは、Javaファイルに含めるマニフェストファイル内の記述ルールです。そして、もう1つは、jarファイル内のフォルダ構造です。

 マニフェストファイルには、リスト1に示すとおり、Multi-Releaseの指定が必要です。

リスト1 Multi-Releaseに対応したjarファイルを指定するためのMANIFEST.MFファイル
Manifest-Version: 1.0
Multi-Release: true // この指定を追加する

 また、jarファイル内のフォルダ構造は図1のようにする必要があります。

図1 Multi-Release Jar Filesのフォルダ構造
図1 Multi-Release Jar Filesのフォルダ構造

 図1内で、versions/9以外にversions/10も記述したのは、この構造はJava 9以上のバージョンに対しても利用可能であることを示すためです。ただし、利用できるバージョン指定はメジャーバージョンのみであるため、9.1や、10.3といった記述はできません。

Multi-Release Jar Filesの作成方法

 Multi-Release Jar Filesに沿ったjarファイルを作るには、Java 9以前の既存のコードと、Java 9向けのコードを用意する必要があります。

 サンプルでは同じクラスをもつJava 8(mrlib8)とJava 9(mrlib9)向けのコードを、図2に示す構造にて2つ用意しました。

図2 サンプルコードのフォルダ・ファイル構造
図2 サンプルコードのフォルダ・ファイル構造

 これら2つのプロジェクトでは同じクラス名を使っていますが、実装はそれぞれ異なっています。例えば、Java 8用に自分自身のプロセスIDを取得するコードを実装したものがリスト2です。

リスト2 Java 8用の自分のプロセスIDを取得するためのコード(mrlib8/src/main/java/com/coltware/mrlib/Process.java)
import java.lang.management.ManagementFactory;

public class Process {
    public static long getPid(){
        String name = ManagementFactory.getRuntimeMXBean().getName();
        int idx = name.indexOf('@');
        if(idx < 0){
            return 0L;
        }
        return Long.parseLong(name.substring(0,idx));
    }
}

 Java 9ではプロセスAPIが新たに追加されているため、JMXを使った実装は必要ありません。

 そこで、Java 9用に記述し直したコードがリスト3です。

リスト3 Java 9用の自分のプロセスIDを取得するためのコード(mrlib9/src/main/java/com/coltware/mrlib/Process.java)
public class Process {
    public static long getPid(){
        ProcessHandle handle = ProcessHandle.current();
        return handle.pid();
    }
}

 このように同じクラス、メソッドとして異なる実装を提供することができます。

 そして、これらのコードからjarファイルを作成する手順がリスト4です。

リスト4 jarファイルを作成する手順(mrlib/build.sh)
## Java 8用のjarファイルを作成
javac --release 8 -d mrlib8/classes $(find mrlib8/src/main/java -name "*.java")  1Java 8用にコンパイル
jar -cfe mrjar.jar com.coltware.Main -C mrlib8/classes .                         2Java 8用のjarファイルの作成

## Java 9向けに変更
javac --release 9 -d mrlib9/classes $(find mrlib9/src/main/java -name "*.java")  3Java 9用にコンパイル
jar -uf  mrjar.jar --release 9 -C mrlib9/classes .   4Java 9用のクラスファイルをjarファイルに追加

 (1)では、Java 8向けにコンパイルをしています。javaコマンドに新たに追加された--releaseオプションを使っていますが、こちらは、今までの-sourceと-targetを同時に指定することとほぼ同様のオプションです。次に(2)では、作成されたclassファイルを使ってjarファイルを作成します。もし、すでにJava 8等で作成されたjarファイルがあればそれを使っても問題ありません。

 そして、(3)ではJava 9向けにコンパイルします。このときにはもちろん、JDK 9を使います。また、ここまでは、Multi-Release向けの作業ではありませんので、実際のプロジェクトではそのプロジェクトに応じたコンパイルのオプションやjarファイルの作成はそれらのプロジェクトに応じて変更してください。

 そして、(4)のjarコマンドで新たに用意された"--release"オプションに"9"を指定し、Java 9向けのコードをjarファイル内にパッケージングします。

 この行程で自動的にMANIFEST.MFファイル内には、Multi-Release指定が追加されますので、Java 9向けにMANIFEST.MFファイルを自分で用意したり編集したりする必要はありません。

 こうして作られたjarファイルのファイルエントリがリスト5です。

リスト5 作成されたjarファイルの中身
     0 META-INF/
   113 META-INF/MANIFEST.MF
     0 com/
     0 com/coltware/
     0 com/coltware/mrlib/
   698 com/coltware/mrlib/Process.class
   311 com/coltware/mrlib/Hello.class
   826 com/coltware/Main.class
     0 META-INF/versions/9/
     0 META-INF/versions/9/com/
     0 META-INF/versions/9/com/coltware/
     0 META-INF/versions/9/com/coltware/mrlib/
   370 META-INF/versions/9/com/coltware/mrlib/Process.class
   311 META-INF/versions/9/com/coltware/mrlib/Hello.class
  1063 META-INF/versions/9/com/coltware/Main.class
   238 META-INF/versions/9/module-info.class

 また、今回jarコマンドにて作成しましたが、自分でjavaコマンドやjarコマンドを使って作業をするのではなく、IDEやMavenもしくはGradleなどを使って作業をしているというケースも多いと思います。

 それらのツールは今回のMulti-Release Jar Filesの機能に対応していないことがありますが、その場合にはjarファイル内のフォルダ構造とマニフェストファイルのルールに準拠した形でjarファイルを作成すれば問題ありません。

 例えば、Gradleであればリスト6のとおり作成することが可能です。

リスト6 作成されたjarファイルの中身
jar {
    from new File("mrlib8/classes")      //  Java 8のclassファイルの場所を指定

    into('META-INF/versions/9') {
        from new File("mrlib9/classes")  //  Java 9のclassコードの場所を指定
    }
    manifest.attributes(
            'Multi-Release': 'true'      //  Multi-Release: trueの指定追加
    )
}

著者プロフィール

  • WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2017年5月時点での登録メンバは52名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

バックナンバー

連載:Java 9で「変わること」と、Javaのこれまで
All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5