module-info.javaのディレクティブまとめ

JavaOne 2016に来ています.
去年はJigsawのセッション取っても,はてなが並ぶばっかりでしたが,意外とJigsawの話が分かるようになってきました.

module-info.javaのディレクティブ(requiresとか)の簡単なまとめです.

exports

module m {
    exports p;
}

packageを他のモジュールからアクセス可能にします.
エクスポートされたパッケージ内のpublicクラスは含むモジュールをリード(後述)できれば,アクセス可能です.

exports to ...

module m {
    exports p to m1, m2;
}

packageを特定のmoduleからのみアクセス可能にします.
...に指定されていないモジュールからはアクセスができません.

exports private

以下のように通常のexportsでは,他のモジュールがprivateなフィールドやメソッドに対してリフレクションでアクセス権を付与しようとすると,実行時例外が発生するようになります.

package m1.p;
class C {
    private String field = "...";
}
module m1 {
    exports m1.p;
}
package m2;
class UsePrivate {
    void method() {
        C c = ...;
        Field field = c.getClass().getDeclaredField("field");
        field.setAccessible(true); // throws Exception
    }
}

exports privateを用いるとこれが可能になります.

module m1 {
    exports m1.p;
}
package m2;
class UsePrivate {
    void method() {
        C c = ...;
        Field field = c.getClass().getDeclaredField("field");
        field.setAccessible(true); // throws Exception
    }
}

exports private to ...

モジュール制限付きexportsかつprivateな要素へのアクセス権限付与を可能にします.

requires

moduleを読み込みます.
requiresするモジュール内でmodule内でエクスポートされたクラスにアクセス可能になります.

requires transitive

推移的なmoduleの読み込みです.
requires transitiveが記述されたモジュールを読み込んだモジュールはrequires transitiveで指定されたmoduleを読み込みます.
モジュールmがあり,モジュールm1がmをrequires transitiveで読み込んでいたとします.

module m {
    exports my.library;
}
module m1 {
    requires transitive m;
}

この時m2がm1を読み込むと,推移的にmも読み込み,my.libraryパッケージのアクセスが可能になります.

module m2 {
    requires m1; // 推移的にmも読み込む
}

requires static

moduleを読み込みますが,コンパイル時のみにmoduleの存在が必須です.
通常のrequiresでは,moduleが存在していなければコンパイル時やランタイム時にエラーとなります.
ランタイム時にmoduleが存在しなくても良い場合に,これを使います.

具体的なユースケースはよく分からないですけど,コンパイル時にのみ有効なアノテーションを使うときとかかなぁ・・・?

参考

Java Platform Module System: Issue Summary
Proposal: #CompileTimeDependences: `requires static`


ここに上げた以外にも,provides withやusesがあります.
それらについてはJigsawでSPIを使用する - きつねとJava!を参考ください.