モジュール
モジュールとは、パッケージを束ねるための単位です。Java 9で追加されました。 モジュールを利用することで、配下のパッケージに対するアクセス権限をより細かに設定できるようになります。具体的には、
・現在のモジュールの中でだけpublic
・特定のモジュールに対してだけpublic
・すべてのモジュールに対してpublic
なパッケージを設定できます。たとえばライブラリをモジュールで束ねることで、そのライブラリの中でだけ利用しているパッケージが、不用意に他のコードからアクセスされることがなくなります(*)。
*)従来のJavaでは、ライブラリ内部でのみ利用するパッケージを不可視にする手段はありませんでした。パッケージプライベートよりも緩い権限が、すぐさま「すべてに対してpublic」だったからです。 標準ライブラリもモジュールJava 9以降では、標準ライブラリもすべてモジュール化されています。具体的には、コマンドラインから以下のコマンドで確認できます。
- > cd C:\pleiades\java\13\bin
- > java –-list-modules
- java.base@13.0.1
- java.compiler@13.0.1
- java.datatransfer@13.0.1
- java.desktop@13.0.1
- java.instrument@13.0.1
- java.logging@13.0.1
- java.management@13.0.1
- java.management.rmi@13.0.1
- java.naming@13.0.1
- java.net.http@13.0.1
- …中略…
- jdk.rmic@13.0.1
- jdk.scripting.nashorn@13.0.1
- jdk.scripting.nashorn.shell@13.0.1
- jdk.sctp@13.0.1
- jdk.security.auth@13.0.1
- jdk.security.jgss@13.0.1
- jdk.unsupported@13.0.1
- jdk.unsupported.desktop@13.0.1
- jdk.xml.dom@13.0.1
- jdk.zipfs@13.0.1
それぞれのパッケージ(クラス)が属するモジュールは、APIリファレンスからも確認できます。
▲現在のクラスが属するモジュールを確認
モジュールの基本モジュールを定義するには、ソースフォルダーのトップにモジュール定義ファイル(module-info.java)を配置するだけです。
module-info.java
module mynavi { }
これでmynaviモジュールを定義したことになります(Eclipseでプロジェクトを作成した場合には、デフォルトでファイルが作成されたことになります)。初期の状態では、{…}の中身は空ですが、最低限モジュールを定義するだけであればこれで十分です。この後、「依存するモジュール」「外部に公開するパッケージ」などを設定する際には、{…}配下にコードを記述していくことになります。
他のモジュールを利用する – requires宣言モジュールの世界では、デフォルトでは異なるモジュールにはアクセスできません。別のモジュールを利用したい場合には、モジュール定義ファイルにrequires宣言を追加してください。たとえば以下は、java.net.httpモジュールを利用する例です(java.net.httpモジュールには、HttpClientなどHTTP通信に関わるクラスが定義されています)。
module-info.java
module mynavi { requires java.net.http; }
requires宣言なしにjava.net.httpモジュールにアクセスした場合、「The type java.net.http.HttpClient is not accessible」のようなエラーとなります。
基本的なコードでrequires宣言を意識しなくてもよかったのは、標準ライブラリのjava.lang、java.ioなど主だったパッケージがjava.baseというモジュールに登録されているからです。java.baseモジュールはrequires宣言なしで無条件にアクセスできるというルールがあります。
推移的な依存関係を宣言する – requires transitive宣言ただし、モジュールが利用しているモジュールをすべて列挙するのは、中々に厄介です。たとえば、モジュールが利用している先のモジュールで利用しているモジュール(=推移的な依存)をすべて把握するのは大概難しいはずです。
しかし、requires transitive宣言では、そうした推移的な依存を表現できます。たとえば、以下のような例です。
module mod1 { requires mod2; } ----------------------------------------------------------------------------- module mod2 { requires transitive java.net.http; requires transitive java.sql; }
この場合、mod1モジュールはmod2モジュール経由でjava.net.http/java.sqlモジュールに依存していますが、mod1側でこれらのモジュールをrequires宣言する必要はありません。これが推移的な依存関係を宣言する、という意味です(よって、太字を削除した場合、mod1モジュールはjava.net.http/java.sqlモジュールを明示的にrequires宣言しなければなりません)。
配下のパッケージを公開する – exports宣言モジュール配下のパッケージは、デフォルトでモジュールプライベート(=モジュールの外からはアクセスできない)と見なされます。モジュールの外からパッケージへのアクセスを許可するには、exportsで公開したいパッケージを宣言してください。
たとえば以下はmynaviモジュールからcom.example.mynavi.basicパッケージを公開する例です。
module mynavi { exports com.example.mynavi.basic; }
特定のモジュールに対してのみパッケージを公開したいならば、exports…to宣言を利用します。たとえば、以下はhoge/piyoモジュールに対してのみcom.example.mynavi.basicパッケージを公開する例です。
module mynavi { exports com.example.mynavi.basic to hoge, piyo; }
モジュールはJava 9で導入された新しいしくみです。よって、暫くは非モジュールなコードとモジュール前提のコードとが混在する状態が続くはずです。このような状態でも、非モジュールをモジュールとして扱えるようにするためのしくみが、自動モジュールです。
具体的には、モジュールパスに配置された.jarファイルで、モジュール定義ファイルを持たないコードは、自動モジュールと見なされます。
モジュールパスとは、モジュールの検索先を表すパスのことです。Eclipseであれば、プロジェクトのプロパティから[Javaのビルド・パス]-[ライブラリー]タブから確認できます。
▲プロジェクトのモジュールパス
自動モジュールの名前は、以下のルールで決定します。
・.jarファイルの名前から拡張子とバージョン番号を除去した上で、非英数字をハイフンで置き換えたもの(hoge-piyo-1.0.1.jarならばhoge.piyoがモジュール名)
・META-INF/MANIFEST.MFのAutomatic-Module-Name属性で宣言された名前(Automatic-Module-Name: hoge.piyo)
自動モジュールはあくまで便宜的なモジュールなので、
・配下のすべてのモジュールを公開(exports)
・モジュールパスに登録されたすべてのモジュールをrequires
したのと同じように動作します。