導入:なぜ「限定公開」が必要なのか
Java 9で導入されたモジュールシステム(Project Jigsaw)の大きな目的の一つは、強力なカプセル化です。通常、exportsキーワードを使うと、そのパッケージは全モジュールに対して公開されます。しかし、大規模なシステム開発では、「特定のモジュールにだけ内部APIを使わせたい」「テストモジュールにのみ公開したい」というケースが頻繁に発生します。これを解決するのが、exports … to … という「限定公開」の仕組みです。本稿では、この機能を正しく使い、堅牢なモジュール設計を行う方法を解説します。
基礎知識:モジュールシステムにおけるアクセス制御
Javaのモジュールは、module-info.javaファイルによってその振る舞いが定義されます。
exportsは、パッケージを他のモジュールから「アクセス可能」にするための命令です。通常、exports com.example.service; と記述すると、他の全モジュールからそのパッケージ内の公開クラスへアクセスできます。
これに対し、qualified exports(限定公開)は、exportsに続けてtoキーワードを用いることで、「特定のモジュール名」を指定し、それ以外のモジュールからのアクセスを遮断します。これにより、内部実装の意図しない利用を防ぎ、依存関係の制御をより厳格に行うことができます。
実装/解決策
限定公開を実装するには、module-info.javaのexports文を以下のように記述します。
構文:
exports パッケージ名 to ターゲットモジュール名1, ターゲットモジュール名2;
この設定を行うと、ターゲットとして指定されたモジュール以外が当該パッケージをインポートしようとした場合、コンパイルエラーや実行時エラーが発生します。これにより、モジュール間の密結合を防ぐことができます。
サンプルプログラム
ここでは、内部APIを保持する「provider」モジュールと、それを利用する「consumer」モジュールの例を示します。
// providerモジュールの module-info.java
module com.example.provider {
// 内部パッケージを「com.example.consumer」モジュールにのみ公開する
exports com.example.provider.internal to com.example.consumer;
}
// 内部パッケージ内のクラス
package com.example.provider.internal;
public class InternalService {
public void execute() {
System.out.println(“限定公開されたサービスが実行されました。”);
}
}
// consumerモジュールの module-info.java
module com.example.consumer {
// providerモジュールが必要であることを宣言
requires com.example.provider;
}
// consumer側の利用例
package com.example.consumer;
import com.example.provider.internal.InternalService;
public class Main {
public static void main(String[] args) {
// コンパイル・実行が可能
InternalService service = new InternalService();
service.execute();
}
}
応用・注意点
現場でこの機能を扱う際の注意点をいくつか挙げます。
1. テストモジュールへの適用: JUnit 5などを使用したテストコードを別モジュールに置く場合、テストモジュールに対してのみexportsを行うことで、本番コードには影響を与えずにテストを実行させることが可能です。
2. opensとの違い: exportsは「コンパイル時の参照」と「実行時のアクセス」を許可しますが、リフレクション(Reflection)によるアクセスを許可したい場合は、opens … to … を使用する必要があります。
3. 循環依存の回避: 限定公開を多用すると、モジュール間の複雑な依存関係が隠蔽され、循環依存が発生しやすくなります。モジュール設計時には、できる限り依存を一方向(非巡回)に保つように注意してください。
限定公開を使いこなすことは、保守性の高いJavaアプリケーションを構築するための第一歩です。まずは「公開範囲を最小限にする」という意識で設計を見直してみてください。

コメント