未来エンジニア養成所Blog

月単価180万以上のプログラミング講師がプログラミングを皆に楽しんでもらうための情報をお届けします。

【Java】モジュールについて

Java9 から追加されたモジュールについて解説します。


モジュールとは

モジュールとは、パッケージを束ねるための単位です。
Java8まではクラスをパッケージに束ねていましたが、そのパッケージをさらに束ねるイメージです。


モジュールを利用すると、モジュール配下のパッケージに対するアクセス権限をより細かに設定できるようになります。


具体的には、

  • 現在のモジュールの中でだけpublic

  • 特定のモジュールに対してだけpublic

  • すべてのモジュールに対してpublic

のような権限をパッケージで設定できます。


たとえば次のような構成を考えてみましょう。

module

パッケージAに含まれるMyUseクラスが、実行用のクラスです。
このクラスはパッケージBのMyServiceクラスを参照しています。
パッケージCにはMyServiceで使うライブラリがまとめられているイメージで、MyUtilクラスをパッケージBには公開しますが、パッケージAには非公開としたいイメージです。


このような場合、MyUseクラスでMyServiceクラスを使用できるように、MyServiceクラスをpublicで宣言します。
さらに、MyServiceクラスでMyUtilクラスを使用できるように、MyUtilクラスをpublicで宣言します。


とすると、MyUseクラスからもMyUtilクラスが使用できる(本来は非公開にしたい)ようになってしまうのです。


このようなケースで適切に権限設定ができるようになったのが「モジュール」です。


標準ライブラリは?

Java 9以降では、標準ライブラリもすべてモジュール化されました。


以下のコマンドで確認してみましょう。
(私の環境はJava11です)

$ java --list-modules
java.base@11.0.9
java.compiler@11.0.9
java.datatransfer@11.0.9
java.desktop@11.0.9
java.instrument@11.0.9
java.logging@11.0.9
java.management@11.0.9
java.management.rmi@11.0.9
java.naming@11.0.9
java.net.http@11.0.9
java.prefs@11.0.9
java.rmi@11.0.9
java.scripting@11.0.9
java.se@11.0.9
java.security.jgss@11.0.9
java.security.sasl@11.0.9
java.smartcardio@11.0.9
java.sql@11.0.9
java.sql.rowset@11.0.9
java.transaction.xa@11.0.9
java.xml@11.0.9
java.xml.crypto@11.0.9
jdk.accessibility@11.0.9
jdk.aot@11.0.9
jdk.attach@11.0.9
jdk.charsets@11.0.9
jdk.compiler@11.0.9
jdk.crypto.cryptoki@11.0.9
jdk.crypto.ec@11.0.9
jdk.dynalink@11.0.9
jdk.editpad@11.0.9
jdk.hotspot.agent@11.0.9
jdk.httpserver@11.0.9
jdk.internal.ed@11.0.9
jdk.internal.jvmstat@11.0.9
jdk.internal.le@11.0.9
jdk.internal.opt@11.0.9
jdk.internal.vm.ci@11.0.9
jdk.internal.vm.compiler@11.0.9
jdk.internal.vm.compiler.management@11.0.9
jdk.jartool@11.0.9
jdk.javadoc@11.0.9
jdk.jcmd@11.0.9
jdk.jconsole@11.0.9
jdk.jdeps@11.0.9
jdk.jdi@11.0.9
jdk.jdwp.agent@11.0.9
jdk.jfr@11.0.9
jdk.jlink@11.0.9
jdk.jshell@11.0.9
jdk.jsobject@11.0.9
jdk.jstatd@11.0.9
jdk.localedata@11.0.9
jdk.management@11.0.9
jdk.management.agent@11.0.9
jdk.management.jfr@11.0.9
jdk.naming.dns@11.0.9
jdk.naming.ldap@11.0.9
jdk.naming.rmi@11.0.9
jdk.net@11.0.9
jdk.pack@11.0.9
jdk.rmic@11.0.9
jdk.scripting.nashorn@11.0.9
jdk.scripting.nashorn.shell@11.0.9
jdk.sctp@11.0.9
jdk.security.auth@11.0.9
jdk.security.jgss@11.0.9
jdk.unsupported@11.0.9
jdk.unsupported.desktop@11.0.9
jdk.xml.dom@11.0.9
jdk.zipfs@11.0.9


また、APIリファレンスでもそれぞれのパッケージ(クラス)が属するモジュールが確認できます。

API


モジュールの基本

モジュールを定義するには、ソースフォルダーのトップにモジュール定義ファイル(module-info.java)を配置するだけです。


eclipseを使用していると、プロジェクトを作成する際にあわせてモジュールも作成されます。


eclipse_module


eclipse_module2


module-info.java

module phoeducation {
}


これでphoeducationモジュールを定義したことになります。
初期の状態では、{…}の中身は空ですが、最低限モジュールを定義するだけであればこれで十分です。
この後、「依存するモジュール」「外部に公開するパッケージ」などを設定する際には、{…}配下にコードを記述していくことになります。


モジュールを使ってみる

プロジェクトの作成

下記3つのプロジェクトを作成します。

  • grandParent

  • parent

  • child


モジュールを作成

各プロジェクトに「module-info.java」ファイルを作成します。
(この時点では{…}の中身は空のままです)


クラスを作成

各プロジェクトにクラスを作成します。


grandParentプロジェクト

HelloGrandParent.java

package grandParent;

public class HelloGrandParent {
    public void hello() {
        System.out.print("Hello! GrandParent!");
    }
}



parentプロジェクト

ParentMain.java

package parent;

import grandParent.HelloGrandParent;

public class ParentMain {

    public static void main(String[] args) {
        HelloGrandParent hgp = new HelloGrandParent();
        hgp.hello();
    }

}


HelloParent.java

package parent;

public class HelloParent {
    public void hello() {
        System.out.println("Hello! Parent!");
    }
}



childプロジェクト

ChildMain.java

package child;

import parent.HelloParent;

public class ChildMain {

    public static void main(String[] args) {
        HelloParent hp = new HelloParent();
        hp.hello();
    }

}


プロジェクトにモジュールパスを設定

eclipseのビルド・パス設定で、モジュールパスを設定します。


parentプロジェクトを右クリックし、「ビルド・パス」→「ビルド・パスの構成」を選択します。 「モジュールパス」にgrandParentを設定します。

parent


chileプロジェクトを右クリックし、「ビルド・パス」→「ビルド・パスの構成」を選択します。 「モジュールパス」にparentを設定します。

parent


module-info.javaを編集

grandParentプロジェクトのパッケージを外部に公開し、parentプロジェクトで読み込めるように設定します。


grandParentプロジェクトのmodule-info.java

module grandParent {
    //grandParentプロジェクトを他のパッケージからアクセスできるようにする
    exports grandParent;
}

exports宣言(配下のパッケージを公開)
モジュール配下のパッケージは、デフォルトでモジュールプライベート(=モジュールの外からはアクセスできない)と見なされます。
モジュールの外からパッケージへのアクセスを許可するには、exportsで公開したいパッケージを宣言する必要があります。



parentプロジェクトのmodule-info.java

module parent {
    //grandParentモジュールを読み込む
    requires grandParent;
}

requires宣言(他のモジュールを利用する)
モジュールの世界では、デフォルトでは異なるモジュールにはアクセスできません。
別のモジュールを利用したい場合には、モジュール定義ファイルにrequires宣言を必要があります。


ParentMain.javaを実行するとgrandParentプロジェクトのHelloGrandParent()メソッドが実行できるようになった事が確認できます。

Hello! GrandParent!


今度はparentプロジェクトのパッケージを外部に公開し、childプロジェクトで読み込めるように設定します。


parentプロジェクトのmodule-info.java

module parent {
    //grandParentモジュールを読み込む
    requires grandParent;

    //parentプロジェクトを他のパッケージからアクセスできるようにする
    exports parent;
}



childプロジェクトのmodule-info.java

module child {
    //parentモジュールを読み込む
    requires parent;
}


ChildMain.javaを実行するとparentプロジェクトのHelloParent()メソッドが実行できるようになった事が確認できます。

Hello! Parent!


モジュールを間接的に読み込む

このままではchildプロジェクトでgrandParentモジュールを使用することはできないため、childプロジェクトでもgrandParentモジュールを使用できるように設定します。


まずはeclipseのビルド・パス設定で、モジュールパスを設定します。


childプロジェクトを右クリックし、「ビルド・パス」→「ビルド・パスの構成」を選択します。 「モジュールパス」にgrandParentを設定します。

parent


parentモジュールで読み込んでいるgrandParentモジュールをchildプロジェクトでも使用できるようにするには「requires transitive」を記述します。



parentプロジェクトのmodule-info.java

module parent {
    //grandParentモジュールを読み込む
    //requires grandParent;            ←コメントアウトしておきます。

    //parentプロジェクトを他のパッケージからアクセスできるようにする
    exports parent;

    //parentモジュールを読み込んだ場合はgrandParentモジュールも使えるようにする
    requires transitive grandParent;
}

requires transitive宣言(推移的な依存関係を宣言する)
モジュールが利用している先のモジュールで利用しているモジュール(=推移的な依存)requires transitive宣言で表現することができます。



ChildMain.java

package child;

import grandParent.HelloGrandParent;
import parent.HelloParent;

public class ChildMain {

    public static void main(String[] args) {
        HelloParent hp = new HelloParent();
        hp.hello();

        //grandParentプロジェクトのメソッド
        HelloGrandParent hgp = new HelloGrandParent();
        hgp.hello();
    }

}


ChildMain.javaを実行するとgrandParentプロジェクトのHelloGrandParent()メソッドも実行できるようになった事が確認できます。

Hello! Parent!
Hello! GrandParent!


まとめ

今回はモジュールの概要について解説しました。


参考図書



さらに学習をしたい場合は、オンラインプログラミングスクール
未来エンジニア養成所Logo