Eclipseで学ぶモジュールシステム

#概要
モジュールを理解するのに苦戦したので、実戦形式でまとめました。
細かな仕様や、概念については別の記事にて作成中ですのでしばらくお待ちください。

IDE：「Eclipse」 Java SE9 以降対応
OS：Windows
コマンドラインとIDEを交えながら説明しますので、他のOSでも応用できるかと思います。
##準備
準備で作成したプロジェクトを何度も改変して解説していきます。
簡単に作る物を説明します。

+ ライブラリ(mylib)
    + TaxCalculator(税金計算機)　貰ったお金から税金を計算し表示するクラス。
    + WorldTaxRate(世界の税率)　各国の税率と国名を列挙型で保持しているクラス。
+ アプリケーション(client)
    + App(実行クラス)　ライブラリを使用して実行するクラス。

###ライブラリの作成(mylib)
1. まずは「mylib」プロジェクトを新規作成します。
2. 次にパッケージとクラスを下記の画像と同じ名前、ディレクトリで作成します。
    1. 新規パッケージ⇒「math.assets.api」と「math.assets.common」をソースフォルダに作成します。
    2. それぞれのパッケージに「TaxCalculator」と「WorldTaxRate」クラスを作成し、下記のソースを貼り付けます。
3. JARファイルの出力場所として「out」フォルダをプロジェクト直下に作成します。

![mylib_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/14d46390-7868-26f0-5a32-3f2e02a146e4.png)

```TaxCalculator.java
package math.assets.api;

import math.assets.common.WorldTaxRate;

public class TaxCalculator{
	public static void taxCalc(int money) {
		System.out.println(money+"$は");
		for(WorldTaxRate wtr : WorldTaxRate.values()) {
			float result = money * wtr.getRate();
			System.out.println(wtr.getContry() + "の税率では"+result+"$になります。");
		}
	}
}
```

```WorldTaxRate.java
package math.assets.common;

public enum WorldTaxRate {
	JAPAN("日本",1.1f),
	CANADA("カナダ", 0.5f),
	FRANCE("フランス", 20.0f);

	private String contry;
	private float rate;

	WorldTaxRate(String contry, float rate) {
		this.contry = contry;
		this.rate = rate;
	}

	public String getContry() {
		return contry;
	}

	public float getRate() {
		return rate;
	}
}
```

###パッケージ化 (JAR化)
1. まずは、作成したクラスをコンパイルします (Eclipseでは保存時に自動でコンパイルされ、binフォルダに各.classが生成される)
2. 次にJARファイルを生成します。
    1. 「mylib」プロジェクトを右クリック。
    1. 「エクスポート」を選択。
    1. 「Java/JARファイル」を選択。
    1. エクスポート先を「mylib/out/mylib.jar」に指定。
    1. 完了を選択し終了。

![エクスポート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/d0fdbaa0-e4eb-52a9-8f50-e96fc855e001.png)

これで「out」フォルダにJARファイルが生成されたかと思います
#####コマンドラインで行う場合
コンパイルのコマンド

```
mylib>javac -d ./bin -encoding utf-8 src/math/assets/api/TaxCalculator.java src/math/assets/common/WorldTaxRate.java
```

パッケージ化のコマンド

```
mylib>jar -cvf ./out/mylib.jar -C bin/ .
```

###アプリケーションの作成(client)
#####【1】プロジェクトの作成
mylibと同様に、「client」プロジェクトを新規作成します。
#####【2】ファイルの作成
1. パッケージとクラスを下記の画像と同じ名前、ディレクトリで作成します。
    1. 新規パッケージ⇒「pkg」をソースフォルダに作成します。
    2. pkgパッケージに「App」クラスを作成し下記のソースを貼り付けます。
2. 使用するライブラリの配置場所として「lib」フォルダを作成します。
    1. libフォルダ内に先ほど生成した「mylib,jar」をコピーします。 

![client_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/db40c143-a620-37fc-edd4-0f5fff9de61d.png)

```App.java
package pkg;

import math.assets.api.TaxCalculator;
import math.assets.common.WorldTaxRate;

public class App {
	public static void main(String[] args) {
		TaxCalculator.taxCalc(1000);
		System.out.println(WorldTaxRate.JAPAN.getContry()); // これを禁止したい
	}
}
```

#####【3】ビルドパス
全ての配置が終了したら、クラスパスにoutフォルダ内の「mylib.jar」を追加します。
1. 「client」プロジェクトを右クリック。
2. 「ビルド・パス」⇒「ビルド・パスの構成」を選択。
3. クラスパスを選択し「JARの追加」を選択。 
4. 先ほどコピーしたlibフォルダ内の「mylib.jar」ファイルを選択し、「OK」を選択。
5. 「適用して閉じる」を選択し終了。下記の画像通りに表示されていれば成功。

![ビルドパス_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/7a6833fe-5087-e8ae-1bbb-59911a142483.png)

####アプリケーションのコンパイル・実行
##### 【1】コンパイル
まずは作成したクラスをコンパイルしていきます。(Eclipseでは自動)

コンパイルのコマンド

```
client>javac -d ./bin -encoding utf-8 -classpath ./lib/mylib.jar ./src/pkg/App.java
```

#####【2】実行
実行結果はこのようになります。

```
client>java -classpath ./bin;./lib/mylib.jar pkg/App

1000$は
日本の税率では1100.0$になります。
カナダの税率では500.0$になります。
フランスの税率では20000.0$になります。
日本

```

##問題点
+ mylibの目的としてはapiパッケージのみを公開したい
    + ⇒commonパッケージも参照されている

この問題を防ぐために**モジュール**を使用します。

##モジュール化(名前付きモジュール)
今回は、
名前付きモジュール(client)--参照-→名前付きモジュール(mylib)
の関係性で解説していきます。
###ライブラリの変更
#####【1】module-info.javaの追加
ソースフォルダ直下に「module-info.java」ファイルを作成します。

1. 「mylib」を右クリック。
1. 「新規」を選択。
1. 「ファイル」を選択。
1. ファイル名を「module-info.java」に変更し、完了を選択

![インフォ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/ab0547ea-735d-4616-1dd6-cee4d5190dce.png)

ディレクトリ
![mylib_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/84c01f96-c158-761c-6285-514cfdcddf71.png)



#####【2】モジュールを記述
今回はapiパッケージのみを外部に公開したいので、apiパッケージのみexportsする。
「module-info.java」に下記のソースを貼り付ける。

```text:module-info.java
module mylib{
	exports math.assets.api;
}
```

#####【3】コンパイル
module-info.javaが追加されたので再コンパイル(Eclipseでは自動コンパイル)
コンパイルコマンド

```
mylib>javac -d ./bin -encoding utf-8 src/module-info.java src/math/assets/api/TaxCalculator.java src/math/assets/common/WorldTaxRate.java
```

binフォルダに「module-info.class」が生成されていることを確認してください。

#####【4】パッケージ化
前回と同じ手順でJARファイルを作成し、「client」の「lib」フォルダにコピーしてください。
outフォルダ内にJARファイルがある場合は上書きしてください。
コマンドラインも前回と同じものが使用できます。

###アプリケーションの変更
#####【1】module-info.javaの追加・記述

1. ソースフォルダ直下に「module-info.java」ファイルを作成します.
1. 作成したmodule-info.javaの中に下記のコードを貼り付けます。

![client_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/fc0829e3-d272-affb-5ef3-0de32d6321e0.png)

```text:module-info.java
module client{
	requires mylib;
}
```
#####【2】ビルドパスを変更
1. 先ほどと同じ手順でビルド・パスの構成を開きます。
1. ライブラリータブのクラスパス内にある「mylib.jar」を選択し、除去を選択します。
1. 「モジュールパス」を選択し、「JARの追加」を選択します。
1. コピーしたlibフォルダ内の「mylib.jar」を選択し、「OK」を選択します。
1. 「適用して閉じる」を選択し終了します。

![クラスパス_1_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/98257f5e-4912-371c-f9a3-35c221ff40c8.jpeg)

#####【3】コンパイル
今回はモジュールパスにモジュールを配置しているので`--module-path`または`-p`を使用します。


```コンパイルコマンド
client>javac -d ./bin -encoding utf-8 --module-path ./lib/mylib.jar; ./src/module-info.java ./src/pkg/App.java
```

#####【4】実行＆修正
このまま実行しようとするとエラーが返却されます。
何故なら、先ほどmylibモジュールにcommonパッケージを公開する(exports)と宣言していないからです。
これにてパッケージの制御は成功となります。
実行するには下記の修正したコードを貼り付けてください。

```App.java
package pkg;

import math.assets.api.TaxCalculator;
//import math.assets.common.WorldTaxRate;	// エラー

public class App {

	public static void main(String[] args) {
		TaxCalculator.taxCalc(1000);
//		System.out.println(WorldTaxRate.JAPAN.getContry()); // エラー
	}
}
```

モジュールを実行するには`--module`または`-m`を使用します。

```:実行コマンド
client>java --module-path ./bin;./lib/mylib.jar; -m client/pkg.App
```
```text:実行結果
1000$は
日本の税率では1100.0$になります。
カナダの税率では500.0$になります。
フランスの税率では20000.0$になります。
```

##無名モジュール
無名モジュールは

+ CLASSPATH上にモジュールがある場合の読込む為の仕組み。
+ モジュールグラフ内のモジュールを全てrequiresする。

という特徴があります。

今回は、
無名モジュール(client)--参照-→名前付きモジュール(mylib)
の関係性で解説していきます。
clientプロジェクトのみ変更を加えていきます。

#####【1】module-info.javaを削除
無名モジュール、自動モジュール共にモジュール宣言をしない為、module-info.javaファイルを削除します。
![client_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/cab4fcbc-3031-0c5d-3ea6-ea185772006f.png)

#####【2】ビルドパスの変更
1. 「ビルドパス」⇒「ビルドパスの構成」を選択
1. モジュールパスにある「mylib.jar」を除去する。
1. クラスパスにlibフォルダ内の「mylib.jar」を追加する。
1. 「適用して閉じる」を選択し終了する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/ecbc661a-a005-d9bc-5c12-12bb8c7f4464.png)

#####【3】実行
モジュールグラフ内のモジュールを全てrequiresする特徴が備わっているので、
commonパッケージを呼び出せてしまいます。
また、クラスパスから呼び出すので`-classpath`を使用します。



```:実行コマンド
client>java -classpath ./bin;./lib/mylib.jar pkg/App
```

```App.java
package pkg;

import math.assets.api.TaxCalculator;
import math.assets.common.WorldTaxRate;	// 読み込めてしまう

public class App {

	public static void main(String[] args) {
		TaxCalculator.taxCalc(1000);
		System.out.println(WorldTaxRate.JAPAN.getContry()); 
	}
}

```
```:実行結果
1000$は
日本の税率では1100.0$になります。
カナダの税率では500.0$になります。
フランスの税率では20000.0$になります。
日本
```

##自動モジュール
自動モジュールは

+ モジュールパス上にモジュール化されていないクラスを読み込む。
+ パッケージを全てexportsする。

という特徴があります。

今回は、
名前付きモジュール(client)--参照-→自動モジュール(mylib)
の関係性で解説していきます。
client、mylibともに変更します。

###ライブラリの変更
#####【1】module-info.javaを削除
無名モジュール、自動モジュール共にモジュール宣言をしない為、module-info.javaファイルを削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/ea343c1b-2891-e6f9-c553-9a8a05b88400.png)

#####【2】パッケージ化
前回と同じ手順でJARファイルを作成し、「client」の「lib」フォルダにコピーしてください。
outフォルダ内にJARファイルがある場合は上書きしてください。

###アプリケーションの変更
#####【1】ビルドパスの変更
1. 「ビルドパス」⇒「ビルドパスの構成」を選択
1. クラスパスにある「mylib.jar」を除去する。
1. モジュールパスにlibフォルダ内の「mylib.jar」を追加する。
1. 「適用して閉じる」を選択し終了する。

#####【2】module-info.javaの追加
ソースフォルダ直下に「module-info.java」ファイルを作成します。

```module-info.java
module client{
	requires mylib;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/fb880293-efc2-bb3f-7348-f7cb024dac88.png)

上記の画像のようにmodule-info.javaに警告が出ていれば成功です。
この警告はというと、
![自動モジュール_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/77dc783e-8a67-e4fc-c942-32ae9b71ed0b.png)

>Google翻訳
>自動モジュール「mylib」の名前は不安定で、モジュールのファイル名から派生しています。

これはclientが自動モジュールに参照したいが、モジュール名を宣言していない為、
Jarファイル名をモジュール名として自動モジュールの名前を仮指定している為に起きる警告です。
試しにmylib.jarファイルの名前を変更してみましょう。

1. 「mylib.jar」⇒「mani-1.2.4.jar」に名前変更。
1. ビルドパスを再度、除去し追加する。

すると、mylibモジュールが見つからない為コンパイルエラーが起きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/652a7df7-db75-a0fd-7baf-9dcec6030bfe.png)

JARファイル名から仮モジュール名を指定しているので「mylib」⇒「mani」に変更すると解決します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413865/a66aa3d0-357a-9762-946b-34db3a7dbc5c.png)
仮モジュール名はバージョン番号や拡張子等が削除されて決まります。

#終わりに
解決できるかはわかりませんが、何かわからないことがあれば
気軽にコメントしてくださると反応いたします。