はじめに
Java SE11(Silver)で新たに試験項目に追加されたモジュールシステムについて、
基本事項をサンプルを交えて説明し、試験で必要そうなポイントを紹介していきたいと思います。
今回は第2回目で、コマンドラインでの操作をメインにモジュールシステムの仕組み、使い方を確認していきます。
初めての方はこちらからどうぞ ⇒ 第1回【概要・準備編】
1.準備
ここからは前回作成したプロジェクトを元に進めていきます。
準備として、クラスパスの設定を取り除きます。
myserviceの場合、「ビルド・パスの構成」で「util.jar」を選択して「除去」をクリックします。
最後に忘れずに「適用して閉じる」をクリックして完了です。
同様に、myclient側でも「service.jar」「util.jar」をクラスパスから取り除いてください。
なお、libフォルダにjarファイルが残っていますが、後で上書きするので消しても消さなくてもOKです。
以下の様に、myclientとmyserviceプロジェクトがコンパイルエラーを吐いていればOKです。
2.myutilのモジュール設定
では、まずはmyutilプロジェクトにモジュールの設定を行います。
①module-info.javaを作成
プロジェクト直下に「module-info.java」を作成しますが、Eclipseの場合の注意点として、
「クラス」ではなく「ファイル」として作成する点に注意してください。
srcフォルダ右クリック
→ 新規 > その他 > 一般 > ファイル
→ ファイル名に「module-info.java」
→ 完了
myutilプロジェクトのsrc直下に作成できていればOKです。
②module-info.javaの中身を記述
さて、module-info.javaの中身を記述していきますが、
ここでモジュールシステムでできることを以下にまとめます。
・どのパッケージを外部に公開するか
・どのモジュールに依存するか
細かいですが覚えておきたい点として、公開の設定はパッケージ、
依存の設定はモジュールを指定することを覚えておきましょう。
これらの設定をするのに具体的にどのように記述すれば良いかですが、
以下に構文をまとめます。
module モジュール名 {
exports パッケージ名;
requires モジュール名;
}
上記の通り、それぞれ「exports」と「requires」を使って記述します。
一行ずつ記載していますが、それぞれ複数記述することも可能です。
では、上記を踏まえてmyutilモジュールを作成してみます。
myutilクラスは他モジュールに依存していない(※補足1)ので、exportsのみ記述します。
module myutil{ exports jp.ssie.ocjp.util; }
これで、MyUtilクラスを含むパッケージを公開する設定が完了です。
【補足1】
他モジュールに依存していないと書きましたが、実際には標準APIを含むパッケージ(java.lang等)
には依存しています。
これら標準APIは「java.base」モジュールにまとめられていて、このモジュールは暗黙的にrequiresされています。
③コンパイルとJAR生成
次にこれをコンパイルします。
eclipseの場合は自動でコンパイルはやってくれるのですが、
今回は試験も意識してコマンドでの操作を見ていきます。
まずはコマンドプロンプトを開きますが、以下から開くとディレクトリ移動の手間が省けます。
プロジェクト右クリック ⇒ コマンドプロンプト
コンパイルは以下のコマンドを使用しました。
javac -d ./bin -encoding utf-8 ./src/module-info.java ./src/jp/ssie/ocjp/util/MyUtil.java
【オプション補足】
-d : コンパイル先のディレクトリ
-encoding : エンコード方式の設定
続けてJARファイルを作成しますが、保存先は前回作成した「jar」フォルダとします。
(記事ではワークスペース直下に作成しています。)
これもコマンドラインでやると、以下になります。
jar -cvf ../jar/util.jar -C bin/ .
【オプション補足】
-c : jarファイルを新規作成
-v : ログ表示
-f : jarファイル名指定
-C : 元ファイル指定
util.jarが作成できていればOKです。
3.myserviceのモジュール設定
次にmyserviceプロジェクトでの設定を行います。
①module-info.javaを作成・中身を記述
手順はmyutilの時と同様で、中身には以下を記述します。
module myservice{ exports jp.ssie.ocjp.service; requires myutil; }
myutilとの違いとして、requiresの行を記述しています。
②util.jarをlibに配置
myutilで作成したutil.jarを、myserviceのlibフォルダに配置しておきましょう。
前回のファイルが残っている場合は上書きしてください。
③コンパイルとJARファイル生成
およそmyutilの場合と同様ですが、モジュールに依存していることでコンパイル時に異なる点があります。
まずはコンパイルですが、myutilと同様にコマンドプロンプトを開いて、
以下のコマンドを実行します。(長くなってきた・・!)
javac -d ./bin -encoding utf-8 --module-path ./lib/util.jar ./src/module-info.java ./src/jp/ssie/ocjp/service/MyService.java
myutilとの違いとして、「--module-path」オプションを追加しています。
これがモジュールパスを設定するオプションで、試験でも問われるかもしれないので以下にまとめます。
依存モジュールパスを指定するオプションです。
複数の場合は、「--module-path パスA;パスB;パスC; 」のようにセミコロンで区切ります。
「-p」も同じ意味のオプションです。
あとはmyutilの時と同様にJARファイルを作成しましょう。
コマンドは以下です。
jar -cvf ../jar/service.jar -C bin/ .
service.jarが作成できていればOKです。
4.myclientのモジュール設定
最後にmyclientプロジェクトでの設定を行います。
①module-info.javaを作成・中身を記述
手順は同様で、中身には以下を記述します。
module myclient{ requires myservice; }
requiresのみを記述します。
②util.jarとservice.jarをlibに配置
util.jarおよびservice.jarを、myclientのlibフォルダに配置しておきましょう。
前回のファイルが残っている場合は上書きしてください。
③コンパイルと実行
コンパイルはMyServiceクラスの時と同様に、モジュールパスを指定しますが、
その前にMyClientクラスで、MyUtilを参照している箇所(14行目)をコメントアウトしておいてください。
package jp.ssie.ocjp.client; import jp.ssie.ocjp.service.MyService; public class MyClient { public static void main(String[] args) { // これがやりたいこと int[] prices = { 100, 200, 300 }; MyService.showTotalPrice(prices); // これができてしまうのがマズい // System.out.println(MyUtil.calcTax(1000)); } }
モジュールを使って制御を変えたことで参照ができなくなっています。
つまりコメントアウトしない場合、コンパイル時にエラーとなります。
コンパイルコマンドは以下です。
javac -d ./bin -encoding utf-8 --module-path ./lib/service.jar;./lib/util.jar; ./src/module-info.java ./src/jp/ssie/ocjp/client/MyClient.java
問題無くコンパイルできていればOKです。
最後に実行は以下のコマンドです。
java --module-path ./bin;./lib/service.jar;./lib/util.jar; -m myclient/jp.ssie.ocjp.client.MyClient
↓ こんな感じに、660が出力されればOKです。(コマンドが見辛い・・)
--module-pathを指定していますが、先頭の「./bin」 はMyClientのコンパイル先となります。
(モジュールパスにはjarの他、コンパイル先ディレクトリを追加することもできます。)
また、-mオプションは実行したいモジュールのクラスを指定しています。
ここは試験でも対象となるかもしれないので、以下にまとめます。
実行したいモジュールのクラスを指定するオプションです。
モジュールに含まれる実行クラスの指定は、モジュール名と完全修飾クラス名の間を「/」で区切ります。
【構文】
java --module-path モジュールのパス -m モジュール名/完全修飾クラス名
確認ポイント
myclientでのコンパイルの際に触れましたが、MyUtilクラスへの参照をした状態ではコンパイルができない、
というのが一番確認したかったポイントです。
つまり、myutilモジュールのmyclientモジュールに対する非公開設定が完成しています。
このように、モジュールを使うとアクセス修飾子だけでは実現できなかった制御が可能となります。
まとめ
なかなか分量が多くなりましたが、やっていること自体は単純です。
モジュールシステムの基本的な挙動、使い方に関しては今回のサンプルを通してイメージできるかと思います。
長くなるので今回はここまでとしますが、次回はもう少し補足的なコマンドや、
試験で対象となりそうなコマンドを紹介したいと思います。