Java9について調べたこと


概要

そろそろJava10(18.3と言うのかな?)が登場するようなので、9で追加された機能で調べた事と試した事を記載しています。

調べたこと

JShell

JShellが導入されました。
対話形式でコマンドラインからコードを実行する事ができます。
簡単なコードを実行したいときに、クラスファイルを使って実行とかの手間を省くことが出来ます。

List.of、Set.of、Map.of

コレクションクラスにファクトリメソッドが追加されました。

List<String> list = List.of("コーヒー牛乳", "いちご牛乳");
Set<String> set = Set.of("れもん牛乳", "めろん牛乳");
Map<String, String> map = Map.of("コーヒー牛乳", "100円", "いちご牛乳", "120円");

ofメソッドで生成したコレクションはイミュータブルになります。

モジュール機能

モジュール機能の導入により、モジュールの公開範囲・依存関係の明確化などが出来るようになりました。
ライブラリなどのpublicメソッドが、使用される事を想定していないクラスで参照されていた・・・
みたいな事も防止する事が出来る。
参考:Project Jigsaw

モジュール機能を使わない場合

呼び出される側(jar)のフォルダ構成(1)
gyunyu/
 ┗━ src/
    ┗━ jp/gr/java_conf/masakado/gyunyu/
       ┣━ Coffee.java
       ┗━ util/
          ┗━ Utility.java
Coffee.java
public class Coffee {
    public void print() {
        Utility.output("コーヒー");
    }
}
java.Utility.java
public class Utility {
    public static void output(String name) {
        System.out.println("これは、" + name + "牛乳です。");
    }
}

Utility.output(...)は、gyunyuプロジェクト内だけで使う事を想定しており、
外部から実行される事を想定していないメソッドです。

上記2ファイルをコンパイルしてgyunyu.jarを作成する。

javac -d bin -encoding UTF-8 src\jp\gr\java_conf\masakado\gyunyu\Coffee.java src\jp\gr\java_conf\masakado\gyunyu\util\Utility.java
jar -cvf gyunyu.jar -C bin .
呼び出し側のフォルダ構成(1)
sample/
 ┣━ src/
 ┃  ┗━ jp/gr/java_conf/masakado/sample/
 ┃     ┗━ Main.java
 ┗━ lib/
    ┗━ gyunyu.jar
Main.java
import jp.gr.java_conf.masakado.gyunyu.Coffee;
import jp.gr.java_conf.masakado.gyunyu.util.Utility;
public class Main {
    public static void main(String[] args) {
        (new Main()).execute();
    }

    public void execute() {
        Coffee coffee = new Coffee();
        coffee.print();
        Utility.output("想定していない呼び出し:いちご"); //外部から実行される事を想定していないメソッドを参照
    }
}

外部から実行される事を想定していないgyunyu.jarUtility.output(...)を参照しています。
これをコンパイルして実行すると

javac -d bin -cp lib\gyunyu.jar -encoding UTF-8  src\jp\gr\java_conf\masakado\sample\Main.java
java -cp lib\gyunyu.jar;bin jp.gr.java_conf.masakado.sample.Main

下記のように表示されます。

モジュール機能を使った場合

呼び出される側(jar)にmodule-info.javaを作成します。

呼び出される側(jar)のフォルダ構成(2)
gyunyu/
 ┗━ src/
    ┣━ jp/gr/java_conf/masakado/gyunyu/
    ┃  ┣━ Coffee.java
    ┃  ┗━ util/
    ┃     ┗━ Utility.java
    ┗━ module-info.java
module-info.java
module jp.gr.java_conf.masakado.gyunyu {
    exports jp.gr.java_conf.masakado.gyunyu;
}

exports : 外部に公開するパッケージを指定する。

モジュール名がパッケージ名と同じになっていますが、パッケージ名に合わせる必要は無く自由に名前を付ける事ができます。
これをコンパイルしてjarファイルを作成。

javac -d bin -encoding UTF-8 src\module-info.java src\jp\gr\java_conf\masakado\gyunyu\Coffee.java src\jp\gr\java_conf\masakado\gyunyu\util\Utility.java
jar -cvf gyunyu.jar -C bin .

作成したgyunyu.jarをsample/libに配置してmodule-info.javaを作成します。

呼び出し側のフォルダ構成(1)
sample/
 ┣━ src/
 ┃  ┣━ jp/gr/java_conf/masakado/sample/
 ┃  ┃  ┗━ Main.java
 ┃  ┗━ module-info.java
 ┗━ lib/
    ┗━ gyunyu.jar
module-info.java
module jp.gr.java_conf.masakado.sample {
    requires jp.gr.java_conf.masakado.gyunyu;
}

requires : 依存する(使用する)モジュールを指定する。

-pオプションでgyunyu.jarを指定してコンパイル。

javac -d bin -p lib\gyunyu.jar -encoding UTF-8 src\module-info.java src\jp\gr\java_conf\masakado\sample\Main.java

下記のようなエラーメッセージが出力され、コンパイルに失敗します。

パッケージjp.gr.java_conf.masakado.gyunyu.utilは表示不可です
(パッケージjp.gr.java_conf.masakado.gyunyu.utilはモジュールjp.gr.java_conf.masakado.gyunyuで宣言されていますが、エクスポートされていません)

Main.javaに記載しているUtility.output(...)を削除するとコンパイルが通るようになります。

モジュールの情報を表示する

下記のように-dオプションを指定してjarコマンドを実行すると、モジュールの情報を参照することができます。

jar -d -f lib\gyunyu.jar
exports jp.gr.java_conf.masakado.gyunyu
requires java.base mandated
contains jp.gr.java_conf.masakado.gyunyu.util

exports : 公開されているパッケージ
requires : 依存しているモジュール
contains : exportsに記載されていないけどjarに含まれているパッケージ

最後に

モジュール機能導入により依存関係が分かりやすくなったため、コンパイル上手くいったから実行してみたらJARが足りずにエラーになった・・・
みたいな事が発生する可能性が減ってくるのかなと感じました。
まだモジュール対応しているJARを扱ったことが無いので、私がモジュール機能の恩恵に与れるのは未だ先の話なのかもしれない。