Java
Java9

JDK9 Jigsaw を試してみた

More than 3 years have passed since last update.

桜庭さんのJAVA IN THE BOXの記事 に触発されて、JDK9で入る予定のProject Jigsawを触ってみました。


Project Jigsawとは

Project Jigsawの概要はここで説明されていました。私の理解では、


  • 不要なライブラリを削ぎ落としたJDKを作ったり、

  • Javaライブラリのアクセス可能性をより細かく設定したり


    • publicクラスにしたら全てのクラスからアクセスできて嫌な感じですよね



というところを目的にしていると思っています。(「パフォーマンスの向上」というのは詳細不明)

また、Jigsawでは、JavaSEにモジュールシステム(module system)というものが導入されます。モジュールとは何ぞやというと、JSR 376によると、以下のように説明されています。

With a module system the packages of a component are grouped into a module that governs how they use other modules, and how other modules use them.

つまり、モジュールとはJavaのパッケージをまとめたもので、他のモジュールをどのように使うか、また他のモジュールからどのように使われるかを制御できるもののようです。

JDKでは、module-info.javaというソースファイルでモジュール間のアクセスを制御できます。


サンプルプログラム

クイックスタートを写経してみました。


呼び出される側のモジュールのソースコード


src/org.astro/module-info.java

module org.astro {

exports org.astro;
}

これは、他のモジュールから、org.astroモジュールに含まれるorg.astroパッケージのpublicクラスのみアクセス可能であることを表します。モジュール名とパッケージ名が同じでややこしいですが、こういう規約でもあるんでしょうかね。


src/org.astro/org/astro/World.java

package org.astro;

public class World {
public static String name() {
return "world";
}
}



呼び出す側のモジュール


src/org.greetings/module-info.java

module com.greetings {

requires org.astro;
}

これは、com.greetingsモジュールが、org.astroモジュールに依存していることを表します。依存しているというのは、com.greetingsモジュールのコンパイル時、あるいは実行時に、org.astroモジュールがモジュールパス上に存在する必要があるということです。


src/org.greetings/com/greetings/Main.java

package com.greetings;

import org.astro.World;

public class Main {
public static void main(String[] args) {
System.out.format("Greetings %s!%n", World.name());
}
}



コンパイル

以下のJavaを使用しました。

>java -version

java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+103-jigsaw-nightly-h4374-20160202)
Java HotSpot(TM) Client VM (build 9-ea+103-jigsaw-nightly-h4374-20160202, mixed mode)

コンパイル方法は以下。org.astroモジュールをコンパイルした後、com.greetingsモジュールをコンパイルしてます。

>javac -d mods\org.astro src\org.astro\module-info.java src\org.astro\org\astro\World.java

>javac -mp mods -d mods\com.greetings src\org.greetings\module-info.java src\org.greetings\com\greetings\Main.java

実は、一度に全てのソースコードをコンパイルできるとクイックスタートにはあるのですが、実際にはなぜかできなかった。Windowsだから?

>javac -d mods -modulesourcepath src src\org.astro\module-info.java src\org.astro\org\astro\World.java src\org.greetings\module-info.java src\org.greetings\com\greetings\Main.java

src\org.greetings\module-info.java:1: エラー: module name com.greetings does not match expected name org.greetings
module com.greetings {
^
エラー1個


実行

>java -mp mods -m com.greetings/com.greetings.Main

Greetings world!

ちゃんと実行できました!

実行対象は、

<モジュール名>/<パッケージパス>.<クラス名>

というように指定します。


パッケージング

モジュールを1つのファイル(jarファイル)にパッケージ化して使用することもできます。

>jar --create --file=mlib\org.astro@1.0.jar --module-version=1.0 -C mods\org.astro .

>jar --create --file=mlib\com.greetings.jar --main-class=com.greetings.Main -C mods\com.greetings .

>dir mlib
com.greetings.jar
org.astro@1.0.jar

モジュールのjarファイルにモジュールパスを通して実行することもできます。ただし、通す先はjarファイルではなくjarファイルの置いてあるディレクトリでOKでした。

>java -mp mlib -m com.greetings/com.greetings.Main

Greetings world!

モジュールの概要を表示してみます。

>jar --print-module-descriptor --file=mlib\org.astro@1.0.jar

Name:
org.astro@1.0
Requires:
java.base [ MANDATED ]
Exports:
org.astro

org.astroモジュールは、java.baseモジュール(Javaプログラムが動作するために必須のモジュール)に依存していて、module-info.javaに書いたようにorg.astroモジュールを公開しています。

>jar --print-module-descriptor --file=mlib\com.greetings.jar

Name:
com.greetings
Requires:
java.base [ MANDATED ]
org.astro
Main class:
com.greetings.Main
Conceals:
com.greetings

一方、com.greetingsモジュールはorg.astroモジュールにも依存しています。また、jarファイルを作るときにメインクラスも指定したため表示されていますね。Concealsは非公開パッケージですかね。(全部出るのかな?)

もちろん、依存するパッケージがなければ、実行に(コンパイルも)失敗します。

>dir mlib2

com.greetings.jar

>java -mp mlib2 -m com.greetings
Error occurred during initialization of VM
java.lang.module.ResolutionException: Module org.astro not found, required by com.greetings
at java.lang.module.Resolver.fail(java.base@9-ea/Resolver.java:860)
at java.lang.module.Resolver.resolve(java.base@9-ea/Resolver.java:351)
at java.lang.module.Resolver.resolve(java.base@9-ea/Resolver.java:305)
at java.lang.module.Configuration.resolve(java.base@9-ea/Configuration.java:249)
at jdk.internal.module.ModuleBootstrap.boot(java.base@9-ea/ModuleBootstrap.java:188)
at java.lang.System.initPhase2(java.base@9-ea/System.java:1917)

今日はここまで。