SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Java 9で「変わること」と、Javaのこれまで

Java 9のモジュール機能「Project Jigsaw」の基本を紹介

Java 9で「変わること」と、Javaのこれまで 第2回

  • X ポスト
  • このエントリーをはてなブックマークに追加

モジュール機能「Project Jigsaw」とは(2)

依存するモジュールが多い場合や依存の依存などがある場合の宣言方法

 利用するパッケージがあるたびに、このような宣言をすることになるのかと思うと少々、面倒に感じるのではないでしょうか。そのため、JavaSEの標準ライブラリでは、java.seモジュールのようにパッケージを集めたモジュールも定義されています。

リスト2 java.seモジュールの定義内容
module java.se {
    requires transitive java.compiler;
    requires transitive java.datatransfer;
    requires transitive java.desktop;
    requires transitive java.instrument;
    requires transitive java.logging;
    requires transitive java.management;
    requires transitive java.naming;
    requires transitive java.prefs;
    requires transitive java.rmi;
    requires transitive java.scripting;
    requires transitive java.security.jgss;
    requires transitive java.security.sasl;
    requires transitive java.sql;
    requires transitive java.sql.rowset;
    requires transitive java.xml;
    requires transitive java.xml.crypto;
}

 また、requireにて宣言したモジュールが、さらに別のモジュールに依存している場合はどうしたらいいでしょうか。もちろん、それらの依存も調べてモジュール内で定義すれば利用できます。

 しかしこれでは大変なので、transitiveをつけて宣言します。この指定をすると、指定したモジュール内で必要とされているモジュールも自動的に宣言したことになります。

 したがって、リスト3のようにtransitiveをつけてjava.seモジュールのrequire宣言するだけで、それらに依存して標準ライブラリで提供されているモジュールを利用できるようになるわけです。

リスト3 java.seモジュールへの依存を宣言方法
module com.coltware.main {
    requires transitive java.se;
}
図5 transitiveの使用により自動的に解決される依存関係
図5 transitiveの使用により自動的に解決される依存関係
 

 ただし、Stringクラスを使うのにもモジュールの宣言が必要ということはありません。それらはjava.baseという基本的なパッケージに含まれているため、宣言する必要はありません。

 これらのJDKのモジュールとパッケージの関係は、Java 9のAPIドキュメントに記載されているのでそちらを参考してください。

実行時にはチェックされない依存を宣言する

  コンパイル時には必要であるものの、実行時には不要な場合、リフレクションを用いて実行する場合があります。このケースでは、リスト4のようにstaticを追加して宣言します。

リスト4 依存の依存を解決する場合の宣言方法例
module com.coltware.main {
	requires static com.colware.mod;
}

 モジュールを使うケースでは多少気をつけないといけない部分はあるものの、基本的には必要なモジュールを宣言していくだけです。実際の開発時に悩むことはあまりないでしょう。

公開するモジュールを定義する

 続いて、自分でモジュールを作成して公開する場合の定義方法です。

 まず、図6のように公開したいパッケージと公開したくないパッケージがある場合を想定してみます。

図6 内部パッケージの隠蔽と公開パッケージの宣言
図6 内部パッケージの隠蔽と公開パッケージの宣言

 

 ライブラリを作っていると、そのライブラリ内でのみ使える内部実装を直接見せたくないケースや、そのライブラリ内で共通に使うユーティリティ的なクラスを含めたくなるケースがあります。また、それらはそのライブラリ内の保守運用において変わる可能性があるため、他のライブラリから読ませないようにするケースもあるでしょう。

 この場合は、リスト5の通りにexoprtsで公開したいパッケージのみ宣言します。

リスト5 公開するパッケージの宣言(sample1/mod/src/com/coltware/mod/module-info.java)
module com.coltware.mod {
    exports com.coltware.mod.api;
}

 公開されていないパッケージを使おうとすると、リスト6のようにコンパイルエラーになります。つまり、そのクラスの存在を知っている開発者であっても、モジュール外から利用することを抑制できます。

リスト6 公開していないパッケージを利用した場合のコンパイラのエラー例
main/src/com/coltware/main/Main.java:9: エラー: パッケージcom.coltware.mod.internalは表示不可です
        com.coltware.mod.internal.InternalHello h;
                        ^
  (パッケージcom.coltware.mod.internalはモジュールcom.coltware.modで宣言されていますが、エクスポートされていません)
エラー1個

 続いて、リスト7のようにメソッドやフィールドに対してディープ・リフレクションを用いている場合です。ディープ・リフレクションとは、setAccessibleを使って、privateメソッドなどを強制的に実行時にアクセスができるようにする機能です。

 しかし、exportsでの指定ではディープ・リフレクションによる実行はできないようになっています。

リスト7 ディープ・リフレクションを用いた実行(sample2/mod/src/com/coltware/mod/ModHello.javaの抜粋)
Class c = Class.forName("com.coltware.submod.SubmodHello");
Object obj = c.getDeclaredConstructor().newInstance();
for(Method m : c.getDeclaredMethods()){
    m.setAccessible(true);  //  ここでprivateなメソッドなどでも強制的にアクセスできるようにしてしまっている
    m.invoke(obj);
}

 実際にこのようなコードをコンパイルしてもエラーは発生しません。しかし、実行時にはリスト8のエラーが発生します。

リスト8 ディープ・リフレクションで実行した場合に発生しうるエラー
java.lang.reflect.InaccessibleObjectException: Unable to make private java.lang.String com.coltware.submod.SubmodHello.exec2() accessible: module com.coltware.submod does not "opens com.coltware.submod" to module com.coltware.mod

 privateなメソッドやフィールドに強制的にアクセスするケースはあまりないはずですが、こうした処理はミドルウェアなどがよく行います。そのため、使う側が定義するということはあまり一般的ではないでしょう。

 したがって、モジュールを定義する側で許可する必要が生じます。この場合は、リスト9のようにopens宣言でモジュールを公開します。

リスト9 ディープ・リフレクションを許可する設定(1)
module com.coltware.submod {
    opens com.coltware.submod;    //  ディープ・リフレクションを許可する指定を追加
    exports com.coltware.submod;
}

 または、リスト10のようにmodule宣言の前にopen宣言しても構いません。

リスト10 ディープ・リフレクションを許可する設定(2)
open module com.coltware.submod {
    exports com.coltware.submod;
}

 ただし、module自体にopen指定をした場合には、exportsしているすべてのパッケージに対してディープ・リフレクションを許可することになってしまいます。この対応が必要になるケースはあまりないはずです。

 また、opensをすると、どんなモジュールからも実行が許可できてしまいます。これでは、そのモジュールを悪用して機能を実行することが容易になってしまいます。

 その場合、リスト11のようにtoを用いることで特定のモジュール(ここではcom.coltware.mod)のみに対して許可することができます。

リスト11 ディープ・リフレクションを許可する設定(3)
module com.coltware.submod {
    exports com.coltware.submod to com.coltware.mod;   // toを用いて利用する側のモジュールを指定
    opens com.coltware.submod to com.coltware.mod;     // toを用いて利用する側のモジュールを指定
}

 このように公開する側で細かく公開範囲の指定ができるようになっています。

 Java 9のモジュール機能が当たり前のように使われるようになると、ミドルウェア上で動作するプログラムを作る場合に、ミドルウェア側のモジュール名の指定が必要になるケースも出てくるでしょう。

 また、ライブラリを作成する側では、公開したパッケージを非公開に変更することは定義上簡単にできますが、実際にそのライブラリを使用している場合、すでに動いていたコードが突然動かなくなるということもあり得ます。

 そのため、安易に公開範囲は変えることはできません。また、公開範囲を無条件に広くしておくことはそれだけ、モジュール機能の意味合いが薄れてきます。

 このように、モジュールを作成して公開する場合には、これまでよりも注意が必要になります。

最後に

 今回は、モジュール機能の基本的な部分のみを紹介しましたが、これだけでも今までのJavaとは制限が大きく異なるということが想像できたのではないでしょうか。また、どうやら、既存のJavaのライブラリをそのまま使うことは難しいと感じられたのではないでしょうか。

 しかし、心配する必要はありません。Java 9が大きく変わっても、既存のリソースが使えないのではJavaの理念である「Write Once, Run Anywhere」から大きく外れることになってしまいます。

 次回は、このように既存の資産が存在する場合の対応や触れなかったサービスである「ServiceLoader」を使って実装を公開する場合など、今回紹介できなかった部分について解説します。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Java 9で「変わること」と、Javaのこれまで連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10524 2017/11/24 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング